Skip to content

Commit

Permalink
Connection limit with error screen (#151)
Browse files Browse the repository at this point in the history
* Connection limit with error screen

* fixes issue preventing tweakpane scrolling

---------

Co-authored-by: Marco Gomez <[email protected]>
  • Loading branch information
MarcusLongmuir and TheCodeTherapy authored Jun 20, 2024
1 parent 26122af commit 0c0b0b6
Show file tree
Hide file tree
Showing 16 changed files with 237 additions and 80 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
7 changes: 6 additions & 1 deletion packages/3d-web-client-core/src/camera/CameraManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,9 @@ export class CameraManager {
}

private onMouseMove(event: MouseEvent): void {
if (getTweakpaneActive()) return;
if (getTweakpaneActive()) {
return;
}
if (this.dragging) {
if (this.targetTheta === null || this.targetPhi === null) return;
this.targetTheta += event.movementX * this.dampingScale;
Expand All @@ -165,6 +167,9 @@ export class CameraManager {
}

private onMouseWheel(event: WheelEvent): void {
if (getTweakpaneActive()) {
return;
}
const scrollAmount = event.deltaY * this.zoomScale * 0.1;
this.targetDistance += scrollAmount;
this.targetDistance = Math.max(
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
41 changes: 24 additions & 17 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 @@ -104,20 +105,21 @@ export class TweakPane {

this.export = this.gui.addFolder({ title: "import / export", expanded: false });

this.setupGUIListeners();
window.addEventListener("keydown", (e) => {
this.processKey(e);
});
this.setupGUIListeners();
}

private setupGUIListeners(): void {
const gui = this.gui as any;
const paneElement: HTMLElement = gui.containerElem_;
paneElement.style.right = this.guiVisible ? "0px" : "-450px";
this.gui.element.addEventListener("mouseenter", () => setTweakpaneActive(true));
this.gui.element.addEventListener("mousemove", () => setTweakpaneActive(true));
this.gui.element.addEventListener("mousedown", () => setTweakpaneActive(true));
this.gui.element.addEventListener("mouseup", () => setTweakpaneActive(false));
this.gui.element.addEventListener("mouseleave", () => setTweakpaneActive(false));
this.gui.element.addEventListener("mouseup", () => setTweakpaneActive(false));
}

private processKey(e: KeyboardEvent): void {
Expand Down Expand Up @@ -177,6 +179,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 @@ -26,6 +27,9 @@ import {
TextChatUIProps,
} 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 @@ -95,6 +99,7 @@ export class Networked3dWebExperienceClient {
private virtualJoystick: VirtualJoystick;

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

private clientId: number | null = null;
private networkClient: UserNetworkingClient;
Expand All @@ -113,6 +118,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 @@ -211,6 +218,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 @@ -243,7 +260,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 @@ -383,7 +400,7 @@ export class Networked3dWebExperienceClient {
}
}
}
requestAnimationFrame(() => {
this.currentRequestAnimationFrame = requestAnimationFrame(() => {
this.update();
});
}
Expand Down Expand Up @@ -422,6 +439,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 @@ -464,6 +506,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
Loading

0 comments on commit 0c0b0b6

Please sign in to comment.