Skip to content

Commit

Permalink
Connection limit with error screen
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcusLongmuir committed Jun 18, 2024
1 parent 5a49f55 commit 400bb3b
Show file tree
Hide file tree
Showing 15 changed files with 228 additions and 77 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,17 @@ export class BasicUserAuthenticator {
): UserData | null {
console.log(`Client ID: ${clientId} joined with token`);
let user = this.userBySessionToken.get(sessionToken);
if (!user && this.options.devAllowUnrecognizedSessions) {
console.warn(`Dev mode: allowing unrecognized session token`);
user = {
clientId: null,
sessionToken,
};
this.userBySessionToken.set(sessionToken, user);
}

if (!user) {
console.error(`Invalid initial user-update for clientId ${clientId}, unknown session`);

if (this.options.devAllowUnrecognizedSessions) {
console.warn(`Dev mode: allowing unrecognized session token`);
user = {
clientId: null,
sessionToken,
};
this.userBySessionToken.set(sessionToken, user);
}
return null;
}

Expand Down
39 changes: 39 additions & 0 deletions packages/3d-web-client-core/src/error-screen/ErrorScreen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
export class ErrorScreen {
public readonly element: HTMLDivElement;

private titleBannerText: HTMLDivElement;
private messageText: HTMLDivElement;

constructor(title: string, message: string) {
this.element = document.createElement("div");
this.element.style.position = "absolute";
this.element.style.top = "0";
this.element.style.left = "0";
this.element.style.display = "flex";
this.element.style.alignItems = "center";
this.element.style.justifyContent = "center";
this.element.style.flexDirection = "column";
this.element.style.width = "100%";
this.element.style.height = "100%";
this.element.style.background = "linear-gradient(45deg, #111111 0%, #444444 100%)";
this.element.style.color = "white";

this.titleBannerText = document.createElement("div");
this.titleBannerText.textContent = title;
this.titleBannerText.style.fontSize = "40px";
this.titleBannerText.style.fontWeight = "bold";
this.titleBannerText.style.fontFamily = "sans-serif";
this.element.append(this.titleBannerText);

this.messageText = document.createElement("div");
this.messageText.style.textAlign = "center";
this.messageText.style.fontFamily = "sans-serif";
this.messageText.style.fontWeight = "bold";
this.messageText.textContent = message;
this.element.append(this.messageText);
}

public dispose() {
this.element.remove();
}
}
1 change: 1 addition & 0 deletions packages/3d-web-client-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ export { CollisionsManager } from "./collisions/CollisionsManager";
export { Sun } from "./sun/Sun";
export { GroundPlane } from "./ground-plane/GroundPlane";
export { LoadingScreen } from "./loading-screen/LoadingScreen";
export { ErrorScreen } from "./error-screen/ErrorScreen";
export { EnvironmentConfiguration } from "./rendering/composer";
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export class LoadingScreen {

private hasCompleted = false;
private loadingCallback: () => void;
private disposed: boolean = false;

constructor(private loadingProgressManager: LoadingProgressManager) {
this.element = document.createElement("div");
Expand Down Expand Up @@ -168,6 +169,10 @@ export class LoadingScreen {
}

public dispose() {
if (this.disposed) {
return;
}
this.disposed = true;
this.loadingProgressManager.removeProgressCallback(this.loadingCallback);
this.element.remove();
}
Expand Down
1 change: 1 addition & 0 deletions packages/3d-web-client-core/src/rendering/composer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@ export class Composer {

public dispose() {
window.removeEventListener("resize", this.resizeListener);
this.renderer.dispose();
}

public fitContainer() {
Expand Down
36 changes: 21 additions & 15 deletions packages/3d-web-client-core/src/tweakpane/TweakPane.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,28 +49,29 @@ export class TweakPane {

private saveVisibilityInLocalStorage: boolean = true;
public guiVisible: boolean = false;
private tweakPaneWrapper: HTMLDivElement;

constructor(
private holderElement: HTMLElement,
private renderer: WebGLRenderer,
private scene: Scene,
private composer: EffectComposer,
) {
const tweakPaneWrapper = document.createElement("div");
tweakPaneWrapper.style.position = "fixed";
tweakPaneWrapper.style.width = "400px";
tweakPaneWrapper.style.height = "100%";
tweakPaneWrapper.style.top = "0px";
tweakPaneWrapper.style.right = "calc(-50vw)";
tweakPaneWrapper.style.zIndex = "99";
tweakPaneWrapper.style.overflow = "auto";
tweakPaneWrapper.style.backgroundColor = "rgba(0, 0, 0, 0.66)";
tweakPaneWrapper.style.paddingLeft = "5px";
tweakPaneWrapper.style.boxShadow = "-7px 0px 12px rgba(0, 0, 0, 0.5)";
tweakPaneWrapper.style.transition = "right cubic-bezier(0.83, 0, 0.17, 1) 0.7s";
holderElement.appendChild(tweakPaneWrapper);

this.gui = new Pane({ container: tweakPaneWrapper! });
this.tweakPaneWrapper = document.createElement("div");
this.tweakPaneWrapper.style.position = "fixed";
this.tweakPaneWrapper.style.width = "400px";
this.tweakPaneWrapper.style.height = "100%";
this.tweakPaneWrapper.style.top = "0px";
this.tweakPaneWrapper.style.right = "calc(-50vw)";
this.tweakPaneWrapper.style.zIndex = "99";
this.tweakPaneWrapper.style.overflow = "auto";
this.tweakPaneWrapper.style.backgroundColor = "rgba(0, 0, 0, 0.66)";
this.tweakPaneWrapper.style.paddingLeft = "5px";
this.tweakPaneWrapper.style.boxShadow = "-7px 0px 12px rgba(0, 0, 0, 0.5)";
this.tweakPaneWrapper.style.transition = "right cubic-bezier(0.83, 0, 0.17, 1) 0.7s";
holderElement.appendChild(this.tweakPaneWrapper);

this.gui = new Pane({ container: this.tweakPaneWrapper! });
this.gui.registerPlugin(EssentialsPlugin);

if (this.saveVisibilityInLocalStorage) {
Expand Down Expand Up @@ -177,6 +178,11 @@ export class TweakPane {
});
}

public dispose() {
this.gui.dispose();
this.tweakPaneWrapper.remove();
}

public setupCamPane(cameraManager: CameraManager) {
this.camera.setupChangeEvent(cameraManager);
}
Expand Down
3 changes: 0 additions & 3 deletions packages/3d-web-client-core/src/tweakpane/tweakPaneStyle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@ export const tweakPaneStyle = `
--tp-label-foreground-color: hsla(0, 0%, 100%, 0.6);
--tp-monitor-background-color: hsla(0, 0%, 0%, 0.3);
--tp-monitor-foreground-color: hsla(0, 0%, 100%, 0.3);
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
}
.tp-brkv {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
Composer,
decodeCharacterAndCamera,
EnvironmentConfiguration,
ErrorScreen,
getSpawnPositionInsideCircle,
GroundPlane,
KeyInputManager,
Expand All @@ -20,6 +21,9 @@ import {
} from "@mml-io/3d-web-client-core";
import { ChatNetworkingClient, FromClientChatMessage, TextChatUI } from "@mml-io/3d-web-text-chat";
import {
AUTHENTICATION_FAILED_ERROR_TYPE,
CONNECTION_LIMIT_REACHED_ERROR_TYPE,
ServerErrorType,
UserData,
UserNetworkingClient,
UserNetworkingClientUpdate,
Expand Down Expand Up @@ -87,6 +91,7 @@ export class Networked3dWebExperienceClient {
private virtualJoystick: VirtualJoystick;

private mmlCompositionScene: MMLCompositionScene;
private mmlFrames: Array<HTMLElement> = [];

private clientId: number | null = null;
private networkClient: UserNetworkingClient;
Expand All @@ -105,6 +110,8 @@ export class Networked3dWebExperienceClient {
private initialLoadCompleted = false;
private loadingProgressManager = new LoadingProgressManager();
private loadingScreen: LoadingScreen;
private errorScreen?: ErrorScreen;
private currentRequestAnimationFrame: number | null = null;

constructor(
private holderElement: HTMLElement,
Expand Down Expand Up @@ -203,6 +210,16 @@ export class Networked3dWebExperienceClient {
characterDescription,
});
},
onServerError: (error: { message: string; errorType: ServerErrorType }) => {
switch (error.errorType) {
case AUTHENTICATION_FAILED_ERROR_TYPE:
this.disposeWithError(error.message);
break;
case CONNECTION_LIMIT_REACHED_ERROR_TYPE:
this.disposeWithError(error.message);
break;
}
},
});

this.characterManager = new CharacterManager({
Expand Down Expand Up @@ -235,7 +252,7 @@ export class Networked3dWebExperienceClient {
this.setupMMLScene();

this.loadingScreen = new LoadingScreen(this.loadingProgressManager);
document.body.append(this.loadingScreen.element);
this.element.append(this.loadingScreen.element);

this.loadingProgressManager.addProgressCallback(() => {
const [, completed] = this.loadingProgressManager.toRatio();
Expand Down Expand Up @@ -372,7 +389,7 @@ export class Networked3dWebExperienceClient {
}
}
}
requestAnimationFrame(() => {
this.currentRequestAnimationFrame = requestAnimationFrame(() => {
this.update();
});
}
Expand Down Expand Up @@ -411,6 +428,31 @@ export class Networked3dWebExperienceClient {
}
}

private disposeWithError(message: string) {
this.dispose();
this.errorScreen = new ErrorScreen("An error occurred", message);
this.element.append(this.errorScreen.element);
}

public dispose() {
this.networkClient.stop();
this.networkChat?.stop();
for (const mmlFrame of this.mmlFrames) {
mmlFrame.remove();
}
this.mmlFrames = [];
this.mmlCompositionScene.dispose();
this.composer.dispose();
this.tweakPane?.dispose();
if (this.currentRequestAnimationFrame !== null) {
cancelAnimationFrame(this.currentRequestAnimationFrame);
this.currentRequestAnimationFrame = null;
}
this.cameraManager.dispose();
this.loadingScreen.dispose();
this.errorScreen?.dispose();
}

private setupMMLScene() {
registerCustomElementsToWindow(window);
this.mmlCompositionScene = new MMLCompositionScene({
Expand Down Expand Up @@ -453,6 +495,7 @@ export class Networked3dWebExperienceClient {
}
}
document.body.appendChild(frameElement);
this.mmlFrames.push(frameElement);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type UserAuthenticator = {
export const defaultSessionTokenPlaceholder = "SESSION.TOKEN.PLACEHOLDER";

export type Networked3dWebExperienceServerConfig = {
connectionLimit?: number;
networkPath: string;
webClientServing: {
indexUrl: string;
Expand Down Expand Up @@ -68,6 +69,7 @@ export class Networked3dWebExperienceServer {
}

this.userNetworkingServer = new UserNetworkingServer({
connectionLimit: config.connectionLimit,
onClientConnect: (
clientId: number,
sessionToken: string,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,15 +124,15 @@ export abstract class ReconnectingWebSocket {
console.warn("Ignoring websocket close event because it is no longer current");
return;
}
console.log("NetworkedDOMWebsocket close", e);
console.log("ReconnectingWebSocket close", e);
onWebsocketClose();
});
websocket.addEventListener("error", (e) => {
if (websocket !== this.websocket) {
console.log("Ignoring websocket error event because it is no longer current");
return;
}
console.error("NetworkedDOMWebsocket error", e);
console.error("ReconnectingWebSocket error", e);
onWebsocketClose();
});

Expand Down
4 changes: 2 additions & 2 deletions packages/3d-web-user-networking/src/ReconnectingWebSocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,15 +139,15 @@ export abstract class ReconnectingWebSocket {
console.warn("Ignoring websocket close event because it is no longer current");
return;
}
console.log("NetworkedDOMWebsocket close", e);
console.log("ReconnectingWebSocket close", e);
onWebsocketClose();
});
websocket.addEventListener("error", (e) => {
if (websocket !== this.websocket) {
console.log("Ignoring websocket error event because it is no longer current");
return;
}
console.error("NetworkedDOMWebsocket error", e);
console.error("ReconnectingWebSocket error", e);
onWebsocketClose();
});

Expand Down
7 changes: 7 additions & 0 deletions packages/3d-web-user-networking/src/UserNetworkingClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
FromServerMessage,
IDENTITY_MESSAGE_TYPE,
PING_MESSAGE_TYPE,
SERVER_ERROR_MESSAGE_TYPE,
ServerErrorType,
USER_AUTHENTICATE_MESSAGE_TYPE,
USER_PROFILE_MESSAGE_TYPE,
} from "./UserNetworkingMessages";
Expand All @@ -23,6 +25,7 @@ export type UserNetworkingClientConfig = {
username: string,
characterDescription: CharacterDescription,
) => void;
onServerError: (error: { message: string; errorType: ServerErrorType }) => void;
};

export class UserNetworkingClient extends ReconnectingWebSocket {
Expand Down Expand Up @@ -51,6 +54,10 @@ export class UserNetworkingClient extends ReconnectingWebSocket {
if (typeof message.data === "string") {
const parsed = JSON.parse(message.data) as FromServerMessage;
switch (parsed.type) {
case SERVER_ERROR_MESSAGE_TYPE:
console.error(`Server error: ${parsed.message}. errorType: ${parsed.errorType}`);
this.config.onServerError(parsed);
break;
case DISCONNECTED_MESSAGE_TYPE:
console.log(`Client ID: ${parsed.id} left`);
this.config.clientUpdate(parsed.id, null);
Expand Down
17 changes: 16 additions & 1 deletion packages/3d-web-user-networking/src/UserNetworkingMessages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export const IDENTITY_MESSAGE_TYPE = "identity";
export const USER_AUTHENTICATE_MESSAGE_TYPE = "user_auth";
export const USER_PROFILE_MESSAGE_TYPE = "user_profile";
export const USER_UPDATE_MESSAGE_TYPE = "user_update";
export const SERVER_ERROR_MESSAGE_TYPE = "error";
export const PING_MESSAGE_TYPE = "ping";
export const PONG_MESSAGE_TYPE = "pong";

Expand Down Expand Up @@ -39,6 +40,19 @@ export type DisconnectedMessage = {
id: number;
};

export const CONNECTION_LIMIT_REACHED_ERROR_TYPE = "CONNECTION_LIMIT_REACHED";
export const AUTHENTICATION_FAILED_ERROR_TYPE = "AUTHENTICATION_FAILED";

export type ServerErrorType =
| typeof CONNECTION_LIMIT_REACHED_ERROR_TYPE
| typeof AUTHENTICATION_FAILED_ERROR_TYPE;

export type ServerError = {
type: typeof SERVER_ERROR_MESSAGE_TYPE;
errorType: ServerErrorType;
message: string;
};

export type FromServerPingMessage = {
type: typeof PING_MESSAGE_TYPE;
};
Expand All @@ -47,7 +61,8 @@ export type FromServerMessage =
| IdentityMessage
| UserProfileMessage
| DisconnectedMessage
| FromServerPingMessage;
| FromServerPingMessage
| ServerError;

export type FromClientPongMessage = {
type: typeof PONG_MESSAGE_TYPE;
Expand Down
Loading

0 comments on commit 400bb3b

Please sign in to comment.