This commit is contained in:
Brian Beck 2026-03-12 16:34:43 -07:00
parent 4741f59582
commit fecf646d90
106 changed files with 1438 additions and 2018 deletions

View file

@ -20,13 +20,15 @@ import { Sky } from "./Sky";
import { AudioEnabled } from "./AudioEnabled";
import type { TorqueObject } from "../torqueScript";
function createLazy<K extends string>(
name: K,
loader: () => Promise<Record<K, React.ComponentType<any>>>,
function createLazy(
name: string,
loader: () => Promise<{ [key: string]: unknown }>,
): React.ComponentType<{ entity: GameEntity }> {
const LazyComponent = lazy(() =>
loader().then((mod) => {
const NamedComponent = mod[name];
const NamedComponent = mod[name] as React.ComponentType<{
entity: GameEntity;
}>;
return { default: NamedComponent };
}),
);
@ -191,10 +193,7 @@ function ShapeEntity({ entity }: { entity: ShapeEntityType }) {
<ShapePlaceholder color="cyan" label={entity.weaponShape} />
}
>
<WeaponModel
shapeName={entity.weaponShape}
playerShapeName={entity.shapeName}
/>
<WeaponModel entity={entity} />
</DebugSuspense>
</ShapeErrorBoundary>
)}

View file

@ -156,7 +156,7 @@ function InteriorMesh({ node }: { node: Mesh }) {
<mesh geometry={node.geometry} castShadow receiveShadow>
{node.material ? (
<DebugSuspense
label={`InteriorTexture:${Array.isArray(node.material) ? node.material[0]?.userData?.resource_path : node.material?.userData?.resource_path ?? "?"}`}
name={`InteriorTexture:${Array.isArray(node.material) ? node.material[0]?.userData?.resource_path : (node.material?.userData?.resource_path ?? "?")}`}
fallback={
// Allow the mesh to render while the texture is still loading;
// show a wireframe placeholder.
@ -262,7 +262,7 @@ export const InteriorInstance = memo(function InteriorInstance({
}}
>
<DebugSuspense
label={`InteriorModel:${scene.interiorFile}`}
name={`InteriorModel:${scene.interiorFile}`}
fallback={<InteriorPlaceholder color="orange" />}
>
<InteriorModel

View file

@ -14,7 +14,7 @@ export function JoinServerButton({
onOpenServerBrowser: () => void;
}) {
const gameStatus = useLiveSelector((s) => s.gameStatus);
const serverName = useLiveSelector((s) => s.serverName);
// const serverName = useLiveSelector((s) => s.serverName);
const ping = useLiveSelector(selectPing);
const disconnectServer = useLiveSelector((s) => s.disconnectServer);
@ -45,7 +45,11 @@ export function JoinServerButton({
<>
<span className={styles.TextLabel}>Live</span>
<span className={styles.ButtonHint}>
{ping != null ? formatPing(ping) : "Join a game"}
{isConnecting
? "Connecting…"
: ping != null
? formatPing(ping)
: "Join a game"}
</span>
</>
{/* {isLive && ping != null && (

View file

@ -570,7 +570,7 @@ export function PlayerModel({ entity }: { entity: PlayerEntity }) {
fallback={<ShapePlaceholder color="red" label={currentWeaponShape} />}
>
<DebugSuspense
label={`Weapon:${entity.id}/${currentWeaponShape}`}
name={`Weapon:${entity.id}/${currentWeaponShape}`}
fallback={
<ShapePlaceholder color="cyan" label={currentWeaponShape} />
}
@ -589,7 +589,7 @@ export function PlayerModel({ entity }: { entity: PlayerEntity }) {
fallback={<ShapePlaceholder color="red" label={currentPackShape} />}
>
<DebugSuspense
label={`Pack:${entity.id}/${currentPackShape}`}
name={`Pack:${entity.id}/${currentPackShape}`}
fallback={
<ShapePlaceholder color="cyan" label={currentPackShape} />
}
@ -604,7 +604,7 @@ export function PlayerModel({ entity }: { entity: PlayerEntity }) {
fallback={<ShapePlaceholder color="red" label={currentFlagShape} />}
>
<DebugSuspense
label={`Flag:${entity.id}/${currentFlagShape}`}
name={`Flag:${entity.id}/${currentFlagShape}`}
fallback={
<ShapePlaceholder color="cyan" label={currentFlagShape} />
}

View file

@ -4,7 +4,6 @@ import {
useCallback,
useContext,
useEffect,
useLayoutEffect,
useMemo,
useRef,
useState,

View file

@ -19,7 +19,7 @@ import type { IflAtlas } from "./useIflTexture";
import { ShapeRenderer, useStaticShape } from "./GenericShape";
import { ShapeInfoProvider } from "./ShapeInfoProvider";
import type { TorqueObject } from "../torqueScript";
import type { ExplosionEntity } from "../state/gameEntityTypes";
import type { ExplosionEntity, ShapeEntity } from "../state/gameEntityTypes";
import { streamPlaybackStore } from "../state/streamPlaybackStore";
/**
@ -44,13 +44,9 @@ function getArmThread(weaponShape: string | undefined): string {
* The mount transform is conjugated by ShapeRenderer's 90° Y rotation:
* T_mount = R90 * M0 * MP^(-1) * R90^(-1).
*/
export function WeaponModel({
shapeName,
playerShapeName,
}: {
shapeName: string;
playerShapeName: string;
}) {
export function WeaponModel({ entity }: { entity: ShapeEntity }) {
const shapeName = entity.weaponShape;
const playerShapeName = entity.shapeName;
const playerGltf = useStaticShape(playerShapeName);
const weaponGltf = useStaticShape(shapeName);

View file

@ -3,7 +3,6 @@ import { Box, useTexture } from "@react-three/drei";
import { useFrame, useThree } from "@react-three/fiber";
import { DoubleSide, NoColorSpace, PlaneGeometry, RepeatWrapping } from "three";
import { textureToUrl } from "../loaders";
import type { SceneWaterBlock } from "../scene/types";
import {
torqueToThree,
torqueScaleToThree,