mirror of
https://github.com/exogen/t2-mapper.git
synced 2026-03-12 08:51:45 +00:00
begin live server support
This commit is contained in:
parent
0c9ddb476a
commit
e4ae265184
368 changed files with 17756 additions and 7738 deletions
147
.dockerignore
Normal file
147
.dockerignore
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
# flyctl launch added from .gitignore
|
||||
# Logs
|
||||
**/logs
|
||||
**/*.log
|
||||
**/npm-debug.log*
|
||||
**/yarn-debug.log*
|
||||
**/yarn-error.log*
|
||||
**/lerna-debug.log*
|
||||
**/.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
**/report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
**/pids
|
||||
**/*.pid
|
||||
**/*.seed
|
||||
**/*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
**/lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
**/coverage
|
||||
**/*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
**/.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
**/.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
**/bower_components
|
||||
|
||||
# node-waf configuration
|
||||
**/.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
**/build/Release
|
||||
|
||||
# Dependency directories
|
||||
**/node_modules
|
||||
**/jspm_packages
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
**/web_modules
|
||||
|
||||
# TypeScript cache
|
||||
**/*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
**/.npm
|
||||
|
||||
# Optional eslint cache
|
||||
**/.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
**/.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
**/.rpt2_cache
|
||||
**/.rts2_cache_cjs
|
||||
**/.rts2_cache_es
|
||||
**/.rts2_cache_umd
|
||||
|
||||
# Optional REPL history
|
||||
**/.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
**/*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
**/.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
**/.env
|
||||
**/.env.development.local
|
||||
**/.env.test.local
|
||||
**/.env.production.local
|
||||
**/.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
**/.cache
|
||||
**/.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
**/.next
|
||||
**/out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
**/.nuxt
|
||||
**/dist
|
||||
!.yalc/**/dist
|
||||
|
||||
# Gatsby files
|
||||
**/.cache
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
**/.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
**/.temp
|
||||
**/.cache
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
**/.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
**/.serverless
|
||||
|
||||
# FuseBox cache
|
||||
**/.fusebox
|
||||
|
||||
# DynamoDB Local files
|
||||
**/.dynamodb
|
||||
|
||||
# TernJS port file
|
||||
**/.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
**/.vscode-test
|
||||
|
||||
# yarn v2
|
||||
**/.yarn/cache
|
||||
**/.yarn/unplugged
|
||||
**/.yarn/build-state.yml
|
||||
**/.yarn/install-state.gz
|
||||
**/.pnp.*
|
||||
|
||||
**/.DS_Store
|
||||
**/.tshy
|
||||
|
||||
# yalc
|
||||
#**/.yalc
|
||||
|
||||
# GameData folder with actual .vl2 files (unextracted) in it. Some scripts like
|
||||
# `extract-assets` and `generate-manifest` look at this to extract or build the
|
||||
# list of files. Once someone builds this, it's not really necessary for other
|
||||
# developers to have this folder.
|
||||
**/GameData
|
||||
|
||||
**/docs/dev
|
||||
fly.toml
|
||||
26
.env.example
Normal file
26
.env.example
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
# Tribes 2 account credentials.
|
||||
# Only username and password are needed — run `npm run login` to download
|
||||
# the certificate and encrypted key from the TribesNext auth server.
|
||||
# The login script writes everything to .env.local automatically.
|
||||
T2_ACCOUNT_NAME=
|
||||
T2_ACCOUNT_PASSWORD=
|
||||
|
||||
# These are populated automatically by `npm run login`:
|
||||
# T2_ACCOUNT_CERTIFICATE=
|
||||
# T2_ACCOUNT_ENCRYPTED_KEY=
|
||||
|
||||
# Server addresses (defaults shown)
|
||||
# T2_AUTH_SERVER=tribesnext.thyth.com:60659
|
||||
# T2_MASTER_SERVER=master.tribesnext.com
|
||||
|
||||
# Relay WebSocket port (default: 8765)
|
||||
# RELAY_PORT=8765
|
||||
|
||||
# Path to game assets (default: docs/base relative to relay/)
|
||||
# GAME_BASE_PATH=/data/base
|
||||
|
||||
# Path to manifest.json (default: public/manifest.json relative to project root)
|
||||
# MANIFEST_PATH=/app/public/manifest.json
|
||||
|
||||
# Relay URL for the browser client (default: ws://localhost:8765)
|
||||
# NEXT_PUBLIC_RELAY_URL=ws://localhost:8765
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -76,6 +76,7 @@ web_modules/
|
|||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
|
|
@ -132,6 +133,9 @@ dist
|
|||
.DS_Store
|
||||
.tshy
|
||||
|
||||
# yalc
|
||||
.yalc
|
||||
|
||||
# GameData folder with actual .vl2 files (unextracted) in it. Some scripts like
|
||||
# `extract-assets` and `generate-manifest` look at this to extract or build the
|
||||
# list of files. Once someone builds this, it's not really necessary for other
|
||||
|
|
|
|||
14
CLAUDE.md
14
CLAUDE.md
|
|
@ -81,6 +81,20 @@ to a React component tree.
|
|||
more). Refer to official documentation or the actual Torque3D SDK, which
|
||||
contains the official grammar and source code. Check the `TorqueEngineResources`
|
||||
folder for related materials.
|
||||
- Normally, most TorqueScript `.cs` files live in VL2 files (deflate encoded,
|
||||
basically .zip) inside Tribes 2's various folders. For this project, since we
|
||||
don't want to have to decompress those VL2 files all the time, they've been
|
||||
pre-extracted. You can find these under the `docs/base` folder.
|
||||
|
||||
### t2-demo-parser
|
||||
|
||||
- This is a custom package developed locally and published via `yalc`.
|
||||
- If you need to see how it works, I don't recommend reading its `dist` files;
|
||||
instead focus on its `src` files and README.
|
||||
- You may update it, but be careful. Verify any changes you make carefully and
|
||||
add regression tests.
|
||||
- To make changes visible to `t2-mapper`, make sure you run `npm run build` and
|
||||
`yalc push` in `t2-demo-parser`.
|
||||
|
||||
## Code Conventions
|
||||
|
||||
|
|
|
|||
87
README.md
87
README.md
|
|
@ -39,6 +39,93 @@ Run the dev server:
|
|||
npm start
|
||||
```
|
||||
|
||||
### Relay Server
|
||||
|
||||
The relay server bridges WebSocket connections from the browser to Tribes 2 game
|
||||
servers via UDP. This is necessary because browsers can't open UDP sockets
|
||||
directly.
|
||||
|
||||
#### Local development
|
||||
|
||||
First, obtain TribesNext account credentials:
|
||||
|
||||
```console
|
||||
npm run login
|
||||
```
|
||||
|
||||
This prompts for your TribesNext username and password, downloads the account
|
||||
certificate and encrypted key, and writes them to `.env.local`.
|
||||
|
||||
Then run the relay (or use `npm run start:both` to run it alongside the Next.js
|
||||
dev server):
|
||||
|
||||
```console
|
||||
npm run relay:dev
|
||||
```
|
||||
|
||||
#### Deploying to Fly.io
|
||||
|
||||
The relay is configured for [Fly.io](https://fly.io) deployment via
|
||||
`relay/Dockerfile` and `fly.toml`. It needs a persistent volume to hold game
|
||||
assets (~1.5 GB) used for the CRC integrity check.
|
||||
|
||||
**1. Create the app and volume:**
|
||||
|
||||
```console
|
||||
fly launch # creates the app (adjust app name in fly.toml if needed)
|
||||
fly volumes create gamedata --region ord --size 3
|
||||
```
|
||||
|
||||
**2. Set account credentials as secrets:**
|
||||
|
||||
Run `npm run login` locally first if you haven't already, then copy the values
|
||||
from `.env.local`:
|
||||
|
||||
```console
|
||||
fly secrets set \
|
||||
T2_ACCOUNT_NAME=... \
|
||||
T2_ACCOUNT_PASSWORD=... \
|
||||
T2_ACCOUNT_CERTIFICATE=... \
|
||||
T2_ACCOUNT_ENCRYPTED_KEY=...
|
||||
```
|
||||
|
||||
**3. Deploy:**
|
||||
|
||||
```console
|
||||
fly deploy
|
||||
```
|
||||
|
||||
**4. Populate game assets on the volume:**
|
||||
|
||||
SSH into the running machine and clone the repo to get `docs/base/`:
|
||||
|
||||
```console
|
||||
fly ssh console
|
||||
```
|
||||
|
||||
Then inside the machine:
|
||||
|
||||
```sh
|
||||
apt-get update && apt-get install -y git
|
||||
git clone --depth 1 --filter=blob:none --sparse \
|
||||
https://github.com/exogen/t2-mapper.git /tmp/t2-mapper
|
||||
cd /tmp/t2-mapper
|
||||
git sparse-checkout set docs/base
|
||||
mv docs/base /data/base
|
||||
rm -rf /tmp/t2-mapper
|
||||
```
|
||||
|
||||
This only needs to be done once — the volume persists across deploys.
|
||||
|
||||
**Environment variables** (all optional, with defaults):
|
||||
|
||||
| Variable | Default | Description |
|
||||
| --- | --- | --- |
|
||||
| `RELAY_PORT` | `8765` | WebSocket listen port |
|
||||
| `GAME_BASE_PATH` | `docs/base` relative to relay | Path to extracted game assets |
|
||||
| `MANIFEST_PATH` | `public/manifest.json` relative to project root | Path to resource manifest |
|
||||
| `T2_MASTER_SERVER` | `master.tribesnext.com` | Master server for server list queries |
|
||||
|
||||
### Running scripts
|
||||
|
||||
[tsx](https://tsx.is) is included to run TypeScript files directly.
|
||||
|
|
|
|||
291
app/page.tsx
291
app/page.tsx
|
|
@ -30,18 +30,49 @@ import { AudioProvider } from "@/src/components/AudioContext";
|
|||
import { DebugElements } from "@/src/components/DebugElements";
|
||||
import { CamerasProvider } from "@/src/components/CamerasProvider";
|
||||
import {
|
||||
DemoProvider,
|
||||
useDemoActions,
|
||||
useDemoRecording,
|
||||
} from "@/src/components/DemoProvider";
|
||||
import { DemoPlayback } from "@/src/components/DemoPlayback";
|
||||
import { DemoControls } from "@/src/components/DemoControls";
|
||||
RecordingProvider,
|
||||
usePlaybackActions,
|
||||
useRecording,
|
||||
} from "@/src/components/RecordingProvider";
|
||||
import { EntityScene } from "@/src/components/EntityScene";
|
||||
import { TickProvider } from "@/src/components/TickProvider";
|
||||
import { SceneLighting } from "@/src/components/SceneLighting";
|
||||
import { PlayerHUD } from "@/src/components/PlayerHUD";
|
||||
import { ChatSoundPlayer } from "@/src/components/ChatSoundPlayer";
|
||||
import {
|
||||
LiveConnectionProvider,
|
||||
useLiveConnection,
|
||||
} from "@/src/components/LiveConnection";
|
||||
import { ServerBrowser } from "@/src/components/ServerBrowser";
|
||||
import {
|
||||
FeaturesProvider,
|
||||
useFeatures,
|
||||
} from "@/src/components/FeaturesProvider";
|
||||
|
||||
// Lazy-load demo and live streaming modules — they pull in heavy dependencies
|
||||
// (demo parser, streaming engine, particles) that aren't needed for mission-only mode.
|
||||
const DemoPlayback = lazy(() =>
|
||||
import("@/src/components/DemoPlayback").then((mod) => ({
|
||||
default: mod.DemoPlayback,
|
||||
})),
|
||||
);
|
||||
const DemoPlaybackControls = lazy(() =>
|
||||
import("@/src/components/DemoPlaybackControls").then((mod) => ({
|
||||
default: mod.DemoPlaybackControls,
|
||||
})),
|
||||
);
|
||||
const LiveObserver = lazy(() =>
|
||||
import("@/src/components/LiveObserver").then((mod) => ({
|
||||
default: mod.LiveObserver,
|
||||
})),
|
||||
);
|
||||
const ChatSoundPlayer = lazy(() =>
|
||||
import("@/src/components/ChatSoundPlayer").then((mod) => ({
|
||||
default: mod.ChatSoundPlayer,
|
||||
})),
|
||||
);
|
||||
import {
|
||||
getMissionList,
|
||||
getMissionInfo,
|
||||
findMissionByDemoName,
|
||||
} from "@/src/manifest";
|
||||
import { createParser, parseAsBoolean, useQueryState } from "nuqs";
|
||||
import styles from "./page.module.css";
|
||||
|
|
@ -120,10 +151,36 @@ function MapInspector() {
|
|||
);
|
||||
|
||||
const isTouch = useTouchDevice();
|
||||
const features = useFeatures();
|
||||
const live = useLiveConnection();
|
||||
const { missionName, missionType } = currentMission;
|
||||
const [mapInfoOpen, setMapInfoOpen] = useState(false);
|
||||
const [loadingProgress, setLoadingProgress] = useState(0);
|
||||
const [serverBrowserOpen, setServerBrowserOpen] = useState(false);
|
||||
const [missionLoadingProgress, setMissionLoadingProgress] = useState(0);
|
||||
const [showLoadingIndicator, setShowLoadingIndicator] = useState(true);
|
||||
|
||||
// During live join, show progress based on connection status.
|
||||
// Relay status order: connecting → challenging → authenticating → connected.
|
||||
// Once liveReady (first ghost arrives), loading is complete.
|
||||
const liveLoadingProgress = live.adapter != null
|
||||
? live.liveReady
|
||||
? 1
|
||||
: live.gameStatus === "connected" ? 0.8
|
||||
: live.gameStatus === "authenticating" ? 0.6
|
||||
: live.gameStatus === "challenging" ? 0.3
|
||||
: live.gameStatus === "connecting" ? 0.2
|
||||
: 0.1
|
||||
: null;
|
||||
|
||||
// Reset stale mission progress when live mode takes over, so it can't
|
||||
// flash through if liveLoadingProgress briefly becomes null.
|
||||
useEffect(() => {
|
||||
if (liveLoadingProgress != null) {
|
||||
setMissionLoadingProgress(0);
|
||||
}
|
||||
}, [liveLoadingProgress != null]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const loadingProgress = liveLoadingProgress ?? missionLoadingProgress;
|
||||
const isLoading = loadingProgress < 1;
|
||||
|
||||
// Keep the loading indicator visible briefly after reaching 100%
|
||||
|
|
@ -174,7 +231,7 @@ function MapInspector() {
|
|||
|
||||
const handleLoadingChange = useCallback(
|
||||
(_loading: boolean, progress: number = 0) => {
|
||||
setLoadingProgress(progress);
|
||||
setMissionLoadingProgress(progress);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
|
@ -188,7 +245,7 @@ function MapInspector() {
|
|||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<main>
|
||||
<DemoProvider>
|
||||
<RecordingProvider>
|
||||
<SettingsProvider
|
||||
fogEnabledOverride={fogEnabledOverride}
|
||||
onClearFogEnabledOverride={clearFogEnabledOverride}
|
||||
|
|
@ -221,19 +278,19 @@ function MapInspector() {
|
|||
cameraRef.current = state.camera;
|
||||
}}
|
||||
>
|
||||
<TickProvider>
|
||||
<CamerasProvider>
|
||||
<AudioProvider>
|
||||
<Mission
|
||||
key={`${missionName}~${missionType}`}
|
||||
name={missionName}
|
||||
<MissionWhenIdle
|
||||
missionName={missionName}
|
||||
missionType={missionType}
|
||||
onLoadingChange={handleLoadingChange}
|
||||
/>
|
||||
<SceneLighting />
|
||||
<EntityScene missionType={missionType} />
|
||||
<ObserverCamera />
|
||||
<DebugElements />
|
||||
<DemoPlayback />
|
||||
<ChatSoundPlayer />
|
||||
<DemoAwareControls
|
||||
<StreamingComponents
|
||||
isTouch={isTouch}
|
||||
joystickStateRef={joystickStateRef}
|
||||
joystickZoneRef={joystickZoneRef}
|
||||
|
|
@ -242,9 +299,10 @@ function MapInspector() {
|
|||
/>
|
||||
</AudioProvider>
|
||||
</CamerasProvider>
|
||||
</TickProvider>
|
||||
</Canvas>
|
||||
</div>
|
||||
<PlayerHUD />
|
||||
<StreamingHUD />
|
||||
{isTouch && (
|
||||
<TouchJoystick
|
||||
joystickState={joystickStateRef}
|
||||
|
|
@ -259,6 +317,7 @@ function MapInspector() {
|
|||
missionType={missionType}
|
||||
onChangeMission={changeMission}
|
||||
onOpenMapInfo={() => setMapInfoOpen(true)}
|
||||
onOpenServerBrowser={features.live ? () => setServerBrowserOpen(true) : undefined}
|
||||
cameraRef={cameraRef}
|
||||
isTouch={isTouch}
|
||||
/>
|
||||
|
|
@ -272,83 +331,56 @@ function MapInspector() {
|
|||
/>
|
||||
</Suspense>
|
||||
)}
|
||||
<DemoMissionSync
|
||||
changeMission={changeMission}
|
||||
currentMission={currentMission}
|
||||
<ServerBrowserDialog
|
||||
open={serverBrowserOpen}
|
||||
onClose={() => setServerBrowserOpen(false)}
|
||||
/>
|
||||
<DemoControls />
|
||||
<StreamingOverlay />
|
||||
<DemoWindowAPI />
|
||||
</KeyboardControls>
|
||||
</SettingsProvider>
|
||||
</DemoProvider>
|
||||
</RecordingProvider>
|
||||
</main>
|
||||
</QueryClientProvider>
|
||||
);
|
||||
}
|
||||
|
||||
/** Map from Tribes 2 game type display names to manifest mission type codes. */
|
||||
const GAME_TYPE_TO_MISSION_TYPE: Record<string, string> = {
|
||||
"Capture the Flag": "CTF",
|
||||
"Capture and Hold": "CnH",
|
||||
Deathmatch: "DM",
|
||||
"Team Deathmatch": "TDM",
|
||||
Siege: "Siege",
|
||||
Bounty: "Bounty",
|
||||
Rabbit: "Rabbit",
|
||||
};
|
||||
|
||||
/**
|
||||
* When a demo recording is loaded, switch to the mission it was recorded on.
|
||||
* Only mount Mission (TorqueScript runtime, .mis loading) when NOT streaming.
|
||||
* During demo/live playback, all scene data comes from ghosts — no need for
|
||||
* the heavy TorqueScript execution pipeline.
|
||||
*/
|
||||
function DemoMissionSync({
|
||||
changeMission,
|
||||
currentMission,
|
||||
function MissionWhenIdle({
|
||||
missionName,
|
||||
missionType,
|
||||
onLoadingChange,
|
||||
}: {
|
||||
changeMission: (mission: CurrentMission) => void;
|
||||
currentMission: CurrentMission;
|
||||
missionName: string;
|
||||
missionType: string;
|
||||
onLoadingChange: (isLoading: boolean, progress?: number) => void;
|
||||
}) {
|
||||
const recording = useDemoRecording();
|
||||
const recording = useRecording();
|
||||
const { adapter: liveAdapter } = useLiveConnection();
|
||||
const isStreaming = recording != null || liveAdapter != null;
|
||||
|
||||
useEffect(() => {
|
||||
if (!recording?.missionName) return;
|
||||
if (isStreaming) return null;
|
||||
|
||||
const missionName = findMissionByDemoName(recording.missionName);
|
||||
if (!missionName) {
|
||||
console.warn(
|
||||
`Demo mission "${recording.missionName}" not found in manifest`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const info = getMissionInfo(missionName);
|
||||
const missionTypeCode = recording.gameType
|
||||
? GAME_TYPE_TO_MISSION_TYPE[recording.gameType]
|
||||
: undefined;
|
||||
const missionType =
|
||||
missionTypeCode && info.missionTypes.includes(missionTypeCode)
|
||||
? missionTypeCode
|
||||
: info.missionTypes[0];
|
||||
|
||||
// Skip if we're already on the correct mission to avoid unnecessary
|
||||
// remount cascades (e.g. after a Suspense boundary restores).
|
||||
if (
|
||||
currentMission.missionName === missionName &&
|
||||
currentMission.missionType === missionType
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
changeMission({ missionName, missionType });
|
||||
}, [recording, changeMission, currentMission]);
|
||||
|
||||
return null;
|
||||
return (
|
||||
<Mission
|
||||
key={`${missionName}~${missionType}`}
|
||||
name={missionName}
|
||||
missionType={missionType}
|
||||
onLoadingChange={onLoadingChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables observer/touch controls when a demo recording is loaded so they
|
||||
* don't fight the animated camera.
|
||||
* In-Canvas components that depend on streaming mode. Mounts the appropriate
|
||||
* controller (DemoPlayback or LiveObserver) and disables observer controls
|
||||
* during streaming.
|
||||
*/
|
||||
function DemoAwareControls({
|
||||
function StreamingComponents({
|
||||
isTouch,
|
||||
joystickStateRef,
|
||||
joystickZoneRef,
|
||||
|
|
@ -361,25 +393,96 @@ function DemoAwareControls({
|
|||
lookJoystickStateRef: React.RefObject<JoystickState>;
|
||||
lookJoystickZoneRef: React.RefObject<HTMLDivElement | null>;
|
||||
}) {
|
||||
const recording = useDemoRecording();
|
||||
if (recording) return null;
|
||||
if (isTouch === null) return null;
|
||||
if (isTouch) {
|
||||
return (
|
||||
<TouchCameraMovement
|
||||
joystickState={joystickStateRef}
|
||||
joystickZone={joystickZoneRef}
|
||||
lookJoystickState={lookJoystickStateRef}
|
||||
lookJoystickZone={lookJoystickZoneRef}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return <ObserverControls />;
|
||||
const recording = useRecording();
|
||||
const live = useLiveConnection();
|
||||
const isLive = live.adapter != null;
|
||||
const isStreaming = recording != null || isLive;
|
||||
|
||||
// Show ObserverControls for: non-streaming mode, OR live mode.
|
||||
// During live, ObserverControls provides the same camera controls
|
||||
// (pointer lock, drag-to-rotate, WASD fly) and LiveObserver intercepts
|
||||
// click-while-locked to cycle observed players instead of nextCamera.
|
||||
// During demo playback, the demo drives the camera so no controls needed.
|
||||
const showObserverControls = !isStreaming || isLive;
|
||||
|
||||
return (
|
||||
<>
|
||||
{recording && (
|
||||
<Suspense fallback={null}>
|
||||
<DemoPlayback />
|
||||
</Suspense>
|
||||
)}
|
||||
{isLive && (
|
||||
<Suspense fallback={null}>
|
||||
<LiveObserver />
|
||||
</Suspense>
|
||||
)}
|
||||
{isStreaming && (
|
||||
<Suspense fallback={null}>
|
||||
<ChatSoundPlayer />
|
||||
</Suspense>
|
||||
)}
|
||||
{showObserverControls && isTouch !== null && (
|
||||
isTouch ? (
|
||||
<TouchCameraMovement
|
||||
joystickState={joystickStateRef}
|
||||
joystickZone={joystickZoneRef}
|
||||
lookJoystickState={lookJoystickStateRef}
|
||||
lookJoystickZone={lookJoystickZoneRef}
|
||||
/>
|
||||
) : (
|
||||
<ObserverControls />
|
||||
)
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
/** HUD overlay — shown during streaming (demo or live). */
|
||||
function StreamingHUD() {
|
||||
const recording = useRecording();
|
||||
const live = useLiveConnection();
|
||||
if (!recording && !live.adapter) return null;
|
||||
return <PlayerHUD />;
|
||||
}
|
||||
|
||||
/** Playback controls overlay — only shown during demo playback. */
|
||||
function StreamingOverlay() {
|
||||
const recording = useRecording();
|
||||
const live = useLiveConnection();
|
||||
if (!recording || live.adapter != null) return null;
|
||||
return (
|
||||
<Suspense fallback={null}>
|
||||
<DemoPlaybackControls />
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
/** Server browser dialog connected to live state. */
|
||||
function ServerBrowserDialog({
|
||||
open,
|
||||
onClose,
|
||||
}: {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
}) {
|
||||
const live = useLiveConnection();
|
||||
return (
|
||||
<ServerBrowser
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
servers={live.servers}
|
||||
loading={live.serversLoading}
|
||||
onRefresh={live.listServers}
|
||||
onJoin={(address) => live.joinServer(address)}
|
||||
wsPing={live.wsPing}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
/** Exposes `window.loadDemoRecording` for automation/testing. */
|
||||
function DemoWindowAPI() {
|
||||
const { setRecording } = useDemoActions();
|
||||
const { setRecording } = usePlaybackActions();
|
||||
|
||||
useEffect(() => {
|
||||
window.loadDemoRecording = setRecording;
|
||||
|
|
@ -394,7 +497,11 @@ function DemoWindowAPI() {
|
|||
export default function HomePage() {
|
||||
return (
|
||||
<Suspense>
|
||||
<MapInspector />
|
||||
<FeaturesProvider>
|
||||
<LiveConnectionProvider>
|
||||
<MapInspector />
|
||||
</LiveConnectionProvider>
|
||||
</FeaturesProvider>
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1,11 +1,11 @@
|
|||
1:"$Sreact.fragment"
|
||||
2:I[47257,["/t2-mapper/_next/static/chunks/2f236954d6a65e12.js"],"ClientPageRoot"]
|
||||
3:I[31713,["/t2-mapper/_next/static/chunks/89fcb9c19e93d0ef.js","/t2-mapper/_next/static/chunks/29cbf5720c3c6313.js","/t2-mapper/_next/static/chunks/90c5f23d057a7dda.js","/t2-mapper/_next/static/chunks/c0475cead0a67c33.js","/t2-mapper/_next/static/chunks/07f1e4bb8e7d8066.js","/t2-mapper/_next/static/chunks/d96f10e4606ed566.js","/t2-mapper/_next/static/chunks/44bbdd420cb3ec27.js","/t2-mapper/_next/static/chunks/153d5796298dee1e.js","/t2-mapper/_next/static/chunks/94212136ebe55507.js"],"default"]
|
||||
3:I[31713,["/t2-mapper/_next/static/chunks/89fcb9c19e93d0ef.js","/t2-mapper/_next/static/chunks/2400be5b6a2e4806.js","/t2-mapper/_next/static/chunks/164bc8495505bc95.js","/t2-mapper/_next/static/chunks/14c6376ae0b23060.js","/t2-mapper/_next/static/chunks/153d5796298dee1e.js","/t2-mapper/_next/static/chunks/ee88398bb27ad4a1.js","/t2-mapper/_next/static/chunks/576b06837c0cb7a0.js","/t2-mapper/_next/static/chunks/b07469fc6c6cd3bb.js","/t2-mapper/_next/static/chunks/629d98e413c0344a.js","/t2-mapper/_next/static/chunks/9eaea0ae086bad69.js","/t2-mapper/_next/static/chunks/037fbc56cebf7caa.js","/t2-mapper/_next/static/chunks/1a2c6dc513278881.js","/t2-mapper/_next/static/chunks/49bf5eb2ca42014f.js"],"default"]
|
||||
6:I[97367,["/t2-mapper/_next/static/chunks/2f236954d6a65e12.js"],"OutletBoundary"]
|
||||
7:"$Sreact.suspense"
|
||||
:HL["/t2-mapper/_next/static/chunks/3e57999e46d7efb4.css","style"]
|
||||
:HL["/t2-mapper/_next/static/chunks/3a7943ba4f8effca.css","style"]
|
||||
0:{"buildId":"6xhnTWazjCi9htjLDl3f1","rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/3e57999e46d7efb4.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/3a7943ba4f8effca.css","precedence":"next"}],["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/29cbf5720c3c6313.js","async":true}],["$","script","script-1",{"src":"/t2-mapper/_next/static/chunks/90c5f23d057a7dda.js","async":true}],["$","script","script-2",{"src":"/t2-mapper/_next/static/chunks/c0475cead0a67c33.js","async":true}],["$","script","script-3",{"src":"/t2-mapper/_next/static/chunks/07f1e4bb8e7d8066.js","async":true}],["$","script","script-4",{"src":"/t2-mapper/_next/static/chunks/d96f10e4606ed566.js","async":true}],["$","script","script-5",{"src":"/t2-mapper/_next/static/chunks/44bbdd420cb3ec27.js","async":true}],["$","script","script-6",{"src":"/t2-mapper/_next/static/chunks/153d5796298dee1e.js","async":true}],["$","script","script-7",{"src":"/t2-mapper/_next/static/chunks/94212136ebe55507.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"loading":null,"isPartial":false}
|
||||
:HL["/t2-mapper/_next/static/chunks/12ed5d454f7c6ac3.css","style"]
|
||||
:HL["/t2-mapper/_next/static/chunks/bd03a29a57c8ca45.css","style"]
|
||||
0:{"buildId":"JablvlklHXp4NGWk4TTlC","rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/12ed5d454f7c6ac3.css","precedence":"next"}],["$","link","1",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/bd03a29a57c8ca45.css","precedence":"next"}],["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/2400be5b6a2e4806.js","async":true}],["$","script","script-1",{"src":"/t2-mapper/_next/static/chunks/164bc8495505bc95.js","async":true}],["$","script","script-2",{"src":"/t2-mapper/_next/static/chunks/14c6376ae0b23060.js","async":true}],["$","script","script-3",{"src":"/t2-mapper/_next/static/chunks/153d5796298dee1e.js","async":true}],["$","script","script-4",{"src":"/t2-mapper/_next/static/chunks/ee88398bb27ad4a1.js","async":true}],["$","script","script-5",{"src":"/t2-mapper/_next/static/chunks/576b06837c0cb7a0.js","async":true}],["$","script","script-6",{"src":"/t2-mapper/_next/static/chunks/b07469fc6c6cd3bb.js","async":true}],["$","script","script-7",{"src":"/t2-mapper/_next/static/chunks/629d98e413c0344a.js","async":true}],["$","script","script-8",{"src":"/t2-mapper/_next/static/chunks/9eaea0ae086bad69.js","async":true}],["$","script","script-9",{"src":"/t2-mapper/_next/static/chunks/037fbc56cebf7caa.js","async":true}],["$","script","script-10",{"src":"/t2-mapper/_next/static/chunks/1a2c6dc513278881.js","async":true}],["$","script","script-11",{"src":"/t2-mapper/_next/static/chunks/49bf5eb2ca42014f.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"loading":null,"isPartial":false}
|
||||
4:{}
|
||||
5:"$0:rsc:props:children:0:props:serverProvidedParams:params"
|
||||
8:null
|
||||
|
|
|
|||
|
|
@ -3,16 +3,16 @@
|
|||
3:I[39756,["/t2-mapper/_next/static/chunks/2f236954d6a65e12.js"],"default"]
|
||||
4:I[37457,["/t2-mapper/_next/static/chunks/2f236954d6a65e12.js"],"default"]
|
||||
5:I[47257,["/t2-mapper/_next/static/chunks/2f236954d6a65e12.js"],"ClientPageRoot"]
|
||||
6:I[31713,["/t2-mapper/_next/static/chunks/89fcb9c19e93d0ef.js","/t2-mapper/_next/static/chunks/29cbf5720c3c6313.js","/t2-mapper/_next/static/chunks/90c5f23d057a7dda.js","/t2-mapper/_next/static/chunks/c0475cead0a67c33.js","/t2-mapper/_next/static/chunks/07f1e4bb8e7d8066.js","/t2-mapper/_next/static/chunks/d96f10e4606ed566.js","/t2-mapper/_next/static/chunks/44bbdd420cb3ec27.js","/t2-mapper/_next/static/chunks/153d5796298dee1e.js","/t2-mapper/_next/static/chunks/94212136ebe55507.js"],"default"]
|
||||
6:I[31713,["/t2-mapper/_next/static/chunks/89fcb9c19e93d0ef.js","/t2-mapper/_next/static/chunks/2400be5b6a2e4806.js","/t2-mapper/_next/static/chunks/164bc8495505bc95.js","/t2-mapper/_next/static/chunks/14c6376ae0b23060.js","/t2-mapper/_next/static/chunks/153d5796298dee1e.js","/t2-mapper/_next/static/chunks/ee88398bb27ad4a1.js","/t2-mapper/_next/static/chunks/576b06837c0cb7a0.js","/t2-mapper/_next/static/chunks/b07469fc6c6cd3bb.js","/t2-mapper/_next/static/chunks/629d98e413c0344a.js","/t2-mapper/_next/static/chunks/9eaea0ae086bad69.js","/t2-mapper/_next/static/chunks/037fbc56cebf7caa.js","/t2-mapper/_next/static/chunks/1a2c6dc513278881.js","/t2-mapper/_next/static/chunks/49bf5eb2ca42014f.js"],"default"]
|
||||
9:I[97367,["/t2-mapper/_next/static/chunks/2f236954d6a65e12.js"],"OutletBoundary"]
|
||||
a:"$Sreact.suspense"
|
||||
c:I[97367,["/t2-mapper/_next/static/chunks/2f236954d6a65e12.js"],"ViewportBoundary"]
|
||||
e:I[97367,["/t2-mapper/_next/static/chunks/2f236954d6a65e12.js"],"MetadataBoundary"]
|
||||
10:I[68027,[],"default"]
|
||||
:HL["/t2-mapper/_next/static/chunks/e620039d1c837dab.css","style"]
|
||||
:HL["/t2-mapper/_next/static/chunks/3e57999e46d7efb4.css","style"]
|
||||
:HL["/t2-mapper/_next/static/chunks/3a7943ba4f8effca.css","style"]
|
||||
0:{"P":null,"b":"6xhnTWazjCi9htjLDl3f1","c":["",""],"q":"","i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/e620039d1c837dab.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/89fcb9c19e93d0ef.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"defaultOptions":{"clearOnDefault":false},"children":["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]]}],{"children":[["$","$1","c",{"children":[["$","$L5",null,{"Component":"$6","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@7","$@8"]}}],[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/3e57999e46d7efb4.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/3a7943ba4f8effca.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/29cbf5720c3c6313.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/t2-mapper/_next/static/chunks/90c5f23d057a7dda.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/t2-mapper/_next/static/chunks/c0475cead0a67c33.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/t2-mapper/_next/static/chunks/07f1e4bb8e7d8066.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/t2-mapper/_next/static/chunks/d96f10e4606ed566.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/t2-mapper/_next/static/chunks/44bbdd420cb3ec27.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/t2-mapper/_next/static/chunks/153d5796298dee1e.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/t2-mapper/_next/static/chunks/94212136ebe55507.js","async":true,"nonce":"$undefined"}]],["$","$L9",null,{"children":["$","$a",null,{"name":"Next.MetadataOutlet","children":"$@b"}]}]]}],{},null,false,false]},null,false,false],["$","$1","h",{"children":[null,["$","$Lc",null,{"children":"$Ld"}],["$","div",null,{"hidden":true,"children":["$","$Le",null,{"children":["$","$a",null,{"name":"Next.Metadata","children":"$Lf"}]}]}],null]}],false]],"m":"$undefined","G":["$10",[]],"S":true}
|
||||
:HL["/t2-mapper/_next/static/chunks/12ed5d454f7c6ac3.css","style"]
|
||||
:HL["/t2-mapper/_next/static/chunks/bd03a29a57c8ca45.css","style"]
|
||||
0:{"P":null,"b":"JablvlklHXp4NGWk4TTlC","c":["",""],"q":"","i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/e620039d1c837dab.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/89fcb9c19e93d0ef.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"defaultOptions":{"clearOnDefault":false},"children":["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]]}],{"children":[["$","$1","c",{"children":[["$","$L5",null,{"Component":"$6","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@7","$@8"]}}],[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/12ed5d454f7c6ac3.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/bd03a29a57c8ca45.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/2400be5b6a2e4806.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/t2-mapper/_next/static/chunks/164bc8495505bc95.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/t2-mapper/_next/static/chunks/14c6376ae0b23060.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/t2-mapper/_next/static/chunks/153d5796298dee1e.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/t2-mapper/_next/static/chunks/ee88398bb27ad4a1.js","async":true,"nonce":"$undefined"}],["$","script","script-5",{"src":"/t2-mapper/_next/static/chunks/576b06837c0cb7a0.js","async":true,"nonce":"$undefined"}],["$","script","script-6",{"src":"/t2-mapper/_next/static/chunks/b07469fc6c6cd3bb.js","async":true,"nonce":"$undefined"}],["$","script","script-7",{"src":"/t2-mapper/_next/static/chunks/629d98e413c0344a.js","async":true,"nonce":"$undefined"}],["$","script","script-8",{"src":"/t2-mapper/_next/static/chunks/9eaea0ae086bad69.js","async":true,"nonce":"$undefined"}],["$","script","script-9",{"src":"/t2-mapper/_next/static/chunks/037fbc56cebf7caa.js","async":true,"nonce":"$undefined"}],["$","script","script-10",{"src":"/t2-mapper/_next/static/chunks/1a2c6dc513278881.js","async":true,"nonce":"$undefined"}],["$","script","script-11",{"src":"/t2-mapper/_next/static/chunks/49bf5eb2ca42014f.js","async":true,"nonce":"$undefined"}]],["$","$L9",null,{"children":["$","$a",null,{"name":"Next.MetadataOutlet","children":"$@b"}]}]]}],{},null,false,false]},null,false,false],["$","$1","h",{"children":[null,["$","$Lc",null,{"children":"$Ld"}],["$","div",null,{"hidden":true,"children":["$","$Le",null,{"children":["$","$a",null,{"name":"Next.Metadata","children":"$Lf"}]}]}],null]}],false]],"m":"$undefined","G":["$10",[]],"S":true}
|
||||
7:{}
|
||||
8:"$0:f:0:1:1:children:0:props:children:0:props:serverProvidedParams:params"
|
||||
d:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"}]]
|
||||
|
|
|
|||
|
|
@ -3,4 +3,4 @@
|
|||
3:I[97367,["/t2-mapper/_next/static/chunks/2f236954d6a65e12.js"],"MetadataBoundary"]
|
||||
4:"$Sreact.suspense"
|
||||
5:I[27201,["/t2-mapper/_next/static/chunks/2f236954d6a65e12.js"],"IconMark"]
|
||||
0:{"buildId":"6xhnTWazjCi9htjLDl3f1","rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"MapGenius – Explore maps for Tribes 2"}],["$","meta","1",{"name":"description","content":"Tribes 2 forever."}],["$","link","2",{"rel":"icon","href":"/t2-mapper/icon.png?icon.2911bba1.png","sizes":"108x128","type":"image/png"}],["$","$L5","3",{}]]}]}]}],null]}],"loading":null,"isPartial":false}
|
||||
0:{"buildId":"JablvlklHXp4NGWk4TTlC","rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"MapGenius – Explore maps for Tribes 2"}],["$","meta","1",{"name":"description","content":"Tribes 2 forever."}],["$","link","2",{"rel":"icon","href":"/t2-mapper/icon.png?icon.2911bba1.png","sizes":"108x128","type":"image/png"}],["$","$L5","3",{}]]}]}]}],null]}],"loading":null,"isPartial":false}
|
||||
|
|
|
|||
|
|
@ -3,4 +3,4 @@
|
|||
3:I[39756,["/t2-mapper/_next/static/chunks/2f236954d6a65e12.js"],"default"]
|
||||
4:I[37457,["/t2-mapper/_next/static/chunks/2f236954d6a65e12.js"],"default"]
|
||||
:HL["/t2-mapper/_next/static/chunks/e620039d1c837dab.css","style"]
|
||||
0:{"buildId":"6xhnTWazjCi9htjLDl3f1","rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/e620039d1c837dab.css","precedence":"next"}],["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/89fcb9c19e93d0ef.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"defaultOptions":{"clearOnDefault":false},"children":["$","$L3",null,{"parallelRouterKey":"children","template":["$","$L4",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]]}],"loading":null,"isPartial":false}
|
||||
0:{"buildId":"JablvlklHXp4NGWk4TTlC","rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/e620039d1c837dab.css","precedence":"next"}],["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/89fcb9c19e93d0ef.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"defaultOptions":{"clearOnDefault":false},"children":["$","$L3",null,{"parallelRouterKey":"children","template":["$","$L4",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]]}],"loading":null,"isPartial":false}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
:HL["/t2-mapper/_next/static/chunks/e620039d1c837dab.css","style"]
|
||||
:HL["/t2-mapper/_next/static/chunks/3e57999e46d7efb4.css","style"]
|
||||
:HL["/t2-mapper/_next/static/chunks/3a7943ba4f8effca.css","style"]
|
||||
0:{"buildId":"6xhnTWazjCi9htjLDl3f1","tree":{"name":"","paramType":null,"paramKey":"","hasRuntimePrefetch":false,"slots":{"children":{"name":"__PAGE__","paramType":null,"paramKey":"__PAGE__","hasRuntimePrefetch":false,"slots":null,"isRootLayout":false}},"isRootLayout":true},"staleTime":300}
|
||||
:HL["/t2-mapper/_next/static/chunks/12ed5d454f7c6ac3.css","style"]
|
||||
:HL["/t2-mapper/_next/static/chunks/bd03a29a57c8ca45.css","style"]
|
||||
0:{"buildId":"JablvlklHXp4NGWk4TTlC","tree":{"name":"","paramType":null,"paramKey":"","hasRuntimePrefetch":false,"slots":{"children":{"name":"__PAGE__","paramType":null,"paramKey":"__PAGE__","hasRuntimePrefetch":false,"slots":null,"isRootLayout":false}},"isRootLayout":true},"staleTime":300}
|
||||
|
|
|
|||
1
docs/_next/static/chunks/037fbc56cebf7caa.js
Normal file
1
docs/_next/static/chunks/037fbc56cebf7caa.js
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -1 +0,0 @@
|
|||
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,66027,t=>{"use strict";var e=t.i(69230),r=t.i(69637);function c(t,c){return(0,r.useBaseQuery)(t,e.QueryObserver,c)}t.s(["useQuery",()=>c])},11152,40141,t=>{"use strict";var e=t.i(71645),r={color:void 0,size:void 0,className:void 0,style:void 0,attr:void 0},c=e.default.createContext&&e.default.createContext(r),n=["attr","size","title"];function o(){return(o=Object.assign.bind()).apply(this,arguments)}function i(t,e){var r=Object.keys(t);if(Object.getOwnPropertySymbols){var c=Object.getOwnPropertySymbols(t);e&&(c=c.filter(function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable})),r.push.apply(r,c)}return r}function l(t){for(var e=1;e<arguments.length;e++){var r=null!=arguments[e]?arguments[e]:{};e%2?i(Object(r),!0).forEach(function(e){var c,n,o;c=t,n=e,o=r[e],(n=function(t){var e=function(t,e){if("object"!=typeof t||!t)return t;var r=t[Symbol.toPrimitive];if(void 0!==r){var c=r.call(t,e||"default");if("object"!=typeof c)return c;throw TypeError("@@toPrimitive must return a primitive value.")}return("string"===e?String:Number)(t)}(t,"string");return"symbol"==typeof e?e:e+""}(n))in c?Object.defineProperty(c,n,{value:o,enumerable:!0,configurable:!0,writable:!0}):c[n]=o}):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(r)):i(Object(r)).forEach(function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(r,e))})}return t}function a(t){return r=>e.default.createElement(u,o({attr:l({},t.attr)},r),function t(r){return r&&r.map((r,c)=>e.default.createElement(r.tag,l({key:c},r.attr),t(r.child)))}(t.child))}function u(t){var i=r=>{var c,{attr:i,size:a,title:u}=t,s=function(t,e){if(null==t)return{};var r,c,n=function(t,e){if(null==t)return{};var r={};for(var c in t)if(Object.prototype.hasOwnProperty.call(t,c)){if(e.indexOf(c)>=0)continue;r[c]=t[c]}return r}(t,e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(t);for(c=0;c<o.length;c++)r=o[c],!(e.indexOf(r)>=0)&&Object.prototype.propertyIsEnumerable.call(t,r)&&(n[r]=t[r])}return n}(t,n),f=a||r.size||"1em";return r.className&&(c=r.className),t.className&&(c=(c?c+" ":"")+t.className),e.default.createElement("svg",o({stroke:"currentColor",fill:"currentColor",strokeWidth:"0"},r.attr,i,s,{className:c,style:l(l({color:t.color||r.color},r.style),t.style),height:f,width:f,xmlns:"http://www.w3.org/2000/svg"}),u&&e.default.createElement("title",null,u),t.children)};return void 0!==c?e.default.createElement(c.Consumer,null,t=>i(t)):i(r)}function s(t){return a({tag:"svg",attr:{viewBox:"0 0 288 512"},child:[{tag:"path",attr:{d:"M112 316.94v156.69l22.02 33.02c4.75 7.12 15.22 7.12 19.97 0L176 473.63V316.94c-10.39 1.92-21.06 3.06-32 3.06s-21.61-1.14-32-3.06zM144 0C64.47 0 0 64.47 0 144s64.47 144 144 144 144-64.47 144-144S223.53 0 144 0zm0 76c-37.5 0-68 30.5-68 68 0 6.62-5.38 12-12 12s-12-5.38-12-12c0-50.73 41.28-92 92-92 6.62 0 12 5.38 12 12s-5.38 12-12 12z"},child:[]}]})(t)}function f(t){return a({tag:"svg",attr:{viewBox:"0 0 512 512"},child:[{tag:"path",attr:{d:"M215.03 71.05L126.06 160H24c-13.26 0-24 10.74-24 24v144c0 13.25 10.74 24 24 24h102.06l88.97 88.95c15.03 15.03 40.97 4.47 40.97-16.97V88.02c0-21.46-25.96-31.98-40.97-16.97zM461.64 256l45.64-45.64c6.3-6.3 6.3-16.52 0-22.82l-22.82-22.82c-6.3-6.3-16.52-6.3-22.82 0L416 210.36l-45.64-45.64c-6.3-6.3-16.52-6.3-22.82 0l-22.82 22.82c-6.3 6.3-6.3 16.52 0 22.82L370.36 256l-45.63 45.63c-6.3 6.3-6.3 16.52 0 22.82l22.82 22.82c6.3 6.3 16.52 6.3 22.82 0L416 301.64l45.64 45.64c6.3 6.3 16.52 6.3 22.82 0l22.82-22.82c6.3-6.3 6.3-16.52 0-22.82L461.64 256z"},child:[]}]})(t)}function p(t){return a({tag:"svg",attr:{viewBox:"0 0 576 512"},child:[{tag:"path",attr:{d:"M215.03 71.05L126.06 160H24c-13.26 0-24 10.74-24 24v144c0 13.25 10.74 24 24 24h102.06l88.97 88.95c15.03 15.03 40.97 4.47 40.97-16.97V88.02c0-21.46-25.96-31.98-40.97-16.97zm233.32-51.08c-11.17-7.33-26.18-4.24-33.51 6.95-7.34 11.17-4.22 26.18 6.95 33.51 66.27 43.49 105.82 116.6 105.82 195.58 0 78.98-39.55 152.09-105.82 195.58-11.17 7.32-14.29 22.34-6.95 33.5 7.04 10.71 21.93 14.56 33.51 6.95C528.27 439.58 576 351.33 576 256S528.27 72.43 448.35 19.97zM480 256c0-63.53-32.06-121.94-85.77-156.24-11.19-7.14-26.03-3.82-33.12 7.46s-3.78 26.21 7.41 33.36C408.27 165.97 432 209.11 432 256s-23.73 90.03-63.48 115.42c-11.19 7.14-14.5 22.07-7.41 33.36 6.51 10.36 21.12 15.14 33.12 7.46C447.94 377.94 480 319.54 480 256zm-141.77-76.87c-11.58-6.33-26.19-2.16-32.61 9.45-6.39 11.61-2.16 26.2 9.45 32.61C327.98 228.28 336 241.63 336 256c0 14.38-8.02 27.72-20.92 34.81-11.61 6.41-15.84 21-9.45 32.61 6.43 11.66 21.05 15.8 32.61 9.45 28.23-15.55 45.77-45 45.77-76.88s-17.54-61.32-45.78-76.86z"},child:[]}]})(t)}t.s(["GenIcon",()=>a],40141),t.s(["FaMapPin",()=>s,"FaVolumeMute",()=>f,"FaVolumeUp",()=>p],11152)}]);
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
.GuiMarkup-module__CLgNnq__GuiMarkup{white-space:pre-wrap;font-size:14px;line-height:1.5}.GuiMarkup-module__CLgNnq__GuiMarkup a{color:inherit;text-decoration:underline}.GuiMarkup-module__CLgNnq__Bullet{margin-left:.5em;margin-right:.5em}
|
||||
.MapInfoDialog-module__m0lXla__Dialog{color:#bccec3;-webkit-user-select:text;user-select:text;-webkit-touch-callout:default;background:#142526cc;border:1px solid #41838b99;border-radius:4px;outline:none;grid-template-rows:1fr auto;grid-template-columns:100%;width:800px;max-width:calc(100dvw - 40px);height:600px;max-height:calc(100dvh - 40px);font-size:14px;line-height:1.5;display:grid;position:relative;overflow:hidden;box-shadow:0 0 50px #0006,inset 0 0 60px #01070d99}.MapInfoDialog-module__m0lXla__Overlay{z-index:10;background:#000000b3;justify-content:center;align-items:center;padding:20px;display:flex;position:fixed;inset:0}.MapInfoDialog-module__m0lXla__Body{grid-template-rows:100%;grid-template-columns:1fr auto;min-height:0;display:grid;overflow:hidden}.MapInfoDialog-module__m0lXla__Left{padding:24px 28px;overflow-y:auto}.MapInfoDialog-module__m0lXla__PreviewImage{border-left:1px solid #00bedc40;height:100%;display:block}.MapInfoDialog-module__m0lXla__PreviewImageFloating{float:right;clear:right;width:auto;max-width:30%;max-height:260px;margin:0 0 16px 20px;display:block}.MapInfoDialog-module__m0lXla__Title{color:#7dffff;text-shadow:0 1px 6px #0006;margin:0;font-size:26px;font-weight:500}.MapInfoDialog-module__m0lXla__MapMeta{flex-wrap:wrap;gap:8px 16px;margin-bottom:4px;font-size:15px;font-weight:400;display:flex}.MapInfoDialog-module__m0lXla__MapPlanet{color:#dbcaa8b3}.MapInfoDialog-module__m0lXla__MapQuote{border-left:2px solid #00bedc59;margin:16px 0;padding:0 0 0 14px;font-style:italic}.MapInfoDialog-module__m0lXla__MapQuote p{white-space:pre-line;margin:0 0 4px}.MapInfoDialog-module__m0lXla__MapQuote cite{color:#ffffff73;font-size:12px;font-style:normal;display:block}.MapInfoDialog-module__m0lXla__MapBlurb{margin:0 0 16px;font-size:13px}.MapInfoDialog-module__m0lXla__Section{margin-top:20px}.MapInfoDialog-module__m0lXla__SectionTitle{color:#7dffff;letter-spacing:.04em;text-transform:uppercase;text-shadow:0 0 16px #00d2f040;margin:0 0 8px;font-size:16px;font-weight:500}.MapInfoDialog-module__m0lXla__MusicTrack{color:#cad0ac80;align-items:center;gap:6px;margin-top:16px;font-size:14px;font-style:italic;display:flex}.MapInfoDialog-module__m0lXla__MusicTrack[data-playing=true]{color:#f7fdd8b3}.MapInfoDialog-module__m0lXla__MusicButton{cursor:pointer;color:#557663;opacity:.5;background:0 0;border:0;border-radius:20px;flex-shrink:0;place-content:center;width:32px;height:32px;padding:0;font-size:20px;font-style:normal;line-height:1;display:grid}.MapInfoDialog-module__m0lXla__MusicTrack[data-playing=true] .MapInfoDialog-module__m0lXla__MusicButton{color:#6dffaa;opacity:1}.MapInfoDialog-module__m0lXla__MusicTrack[data-playing=true] .MapInfoDialog-module__m0lXla__MusicButton:hover{opacity:.7}.MapInfoDialog-module__m0lXla__Footer{background:#021415b3;border-top:1px solid #00bedc40;flex-shrink:0;align-items:center;gap:16px;padding:10px 12px;display:flex}.MapInfoDialog-module__m0lXla__CloseButton{color:#9aefe1e6;text-shadow:0 -1px #00000080;cursor:pointer;background:linear-gradient(#29ac9cb3,#005041b3);border:1px solid #29615499;border-top-color:#65b9b080;border-radius:3px;padding:4px 18px;font-size:14px;font-weight:500;box-shadow:inset 0 1px #78dcc333,inset 0 -1px #0000004d,0 2px 4px #0006}.MapInfoDialog-module__m0lXla__CloseButton:active{transform:translateY(1px)}.MapInfoDialog-module__m0lXla__Hint{color:#c9dcd84d;margin-left:auto;font-size:12px}.MapInfoDialog-module__m0lXla__MusicTrackName{text-transform:capitalize}@media (max-width:719px){.MapInfoDialog-module__m0lXla__Body{display:block;overflow:auto}.MapInfoDialog-module__m0lXla__Hint{display:none}.MapInfoDialog-module__m0lXla__Left{width:100%;height:auto;margin:0;padding:16px 20px;overflow:auto}.MapInfoDialog-module__m0lXla__PreviewImage{width:auto;height:auto;margin:16px auto}.MapInfoDialog-module__m0lXla__CloseButton{width:220px;height:36px;margin:0 auto}}
|
||||
89
docs/_next/static/chunks/0c29da8d7ee18a67.js
Normal file
89
docs/_next/static/chunks/0c29da8d7ee18a67.js
Normal file
File diff suppressed because one or more lines are too long
397
docs/_next/static/chunks/12261e943ff623d2.js
Normal file
397
docs/_next/static/chunks/12261e943ff623d2.js
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -1,13 +1,13 @@
|
|||
.FloatingLabel-module__8y09Ka__Label{color:#fff;white-space:nowrap;text-align:center;background:#00000080;border-radius:1px;padding:1px 3px;font-size:11px}
|
||||
.DebugElements-module__Cmeo9W__StatsPanel{bottom:0;right:0;top:auto!important;left:auto!important}.DebugElements-module__Cmeo9W__AxisLabel{pointer-events:none;font-size:12px}.DebugElements-module__Cmeo9W__AxisLabel[data-axis=x]{color:#f90}.DebugElements-module__Cmeo9W__AxisLabel[data-axis=y]{color:#9f0}.DebugElements-module__Cmeo9W__AxisLabel[data-axis=z]{color:#09f}
|
||||
.KeyboardOverlay-module__HsRBsa__Root{pointer-events:none;z-index:1;align-items:flex-end;gap:10px;display:flex;position:fixed;bottom:16px;left:50%;transform:translate(-50%)}.KeyboardOverlay-module__HsRBsa__Column{flex-direction:column;justify-content:center;gap:4px;display:flex}.KeyboardOverlay-module__HsRBsa__Row{justify-content:stretch;gap:4px;display:flex}.KeyboardOverlay-module__HsRBsa__Spacer{width:32px}.KeyboardOverlay-module__HsRBsa__Key{color:#ffffff80;white-space:nowrap;background:#0006;border:1px solid #fff3;border-radius:4px;flex:1 0 0;justify-content:center;align-items:center;min-width:32px;height:32px;padding:0 8px;font-size:11px;font-weight:600;display:flex}.KeyboardOverlay-module__HsRBsa__Key[data-pressed=true]{color:#fff;background:#34bbab99;border-color:#23fddc80}.KeyboardOverlay-module__HsRBsa__Arrow{margin-right:3px}
|
||||
.TouchControls-module__AkxfgW__Joystick{z-index:1;width:140px;height:140px;position:fixed;bottom:20px;left:50%;transform:translate(-50%)}.TouchControls-module__AkxfgW__Left{left:20px;transform:none;}.TouchControls-module__AkxfgW__Right{left:auto;right:20px;transform:none;}
|
||||
.MissionSelect-module__N_AIjG__InputWrapper{align-items:center;display:flex;position:relative}.MissionSelect-module__N_AIjG__Shortcut{color:#fff9;pointer-events:none;background:#ffffff26;border-radius:3px;padding:1px 4px;font-family:system-ui,sans-serif;font-size:11px;position:absolute;right:7px}.MissionSelect-module__N_AIjG__Input[aria-expanded=true]~.MissionSelect-module__N_AIjG__Shortcut{display:none}.MissionSelect-module__N_AIjG__Input{color:#fff;-webkit-user-select:text;user-select:text;background:#0009;border:1px solid #ffffff4d;border-radius:3px;outline:none;width:280px;padding:6px 36px 6px 8px;font-size:14px}.MissionSelect-module__N_AIjG__Input[aria-expanded=true]{padding-right:8px}.MissionSelect-module__N_AIjG__Input:focus{border-color:#fff9}.MissionSelect-module__N_AIjG__Input::placeholder{color:#0000}.MissionSelect-module__N_AIjG__SelectedValue{pointer-events:none;align-items:center;gap:6px;display:flex;position:absolute;left:8px;right:36px;overflow:hidden}.MissionSelect-module__N_AIjG__Input[aria-expanded=true]~.MissionSelect-module__N_AIjG__SelectedValue{display:none}.MissionSelect-module__N_AIjG__SelectedName{color:#fff;white-space:nowrap;text-overflow:ellipsis;flex-shrink:1;min-width:0;font-size:14px;font-weight:600;overflow:hidden}.MissionSelect-module__N_AIjG__SelectedValue>.MissionSelect-module__N_AIjG__ItemType{flex-shrink:0}.MissionSelect-module__N_AIjG__Popover{z-index:100;min-width:320px;max-height:var(--popover-available-height,90vh);overscroll-behavior:contain;background:#141414f2;border:1px solid #ffffff80;border-radius:3px;overflow-y:auto;box-shadow:0 8px 24px #0009}.MissionSelect-module__N_AIjG__List{padding:4px 0}.MissionSelect-module__N_AIjG__List:has(>.MissionSelect-module__N_AIjG__Group:first-child){padding-top:0}.MissionSelect-module__N_AIjG__Group{padding-bottom:4px}.MissionSelect-module__N_AIjG__GroupLabel{color:#c6caca;z-index:1;background:#3a4548f2;border-bottom:1px solid #ffffff4d;padding:6px 8px 6px 12px;font-size:13px;font-weight:600;position:sticky;top:0}.MissionSelect-module__N_AIjG__Group:not(:last-child){border-bottom:1px solid #ffffff4d}.MissionSelect-module__N_AIjG__Item{cursor:pointer;border-radius:4px;outline:none;flex-direction:column;gap:1px;margin:4px 4px 0;padding:6px 8px;scroll-margin-top:32px;display:flex}.MissionSelect-module__N_AIjG__List>.MissionSelect-module__N_AIjG__Item:first-child{margin-top:0}.MissionSelect-module__N_AIjG__Item[data-active-item]{background:#ffffff26}.MissionSelect-module__N_AIjG__Item[aria-selected=true]{background:#6496ff4d}.MissionSelect-module__N_AIjG__ItemHeader{align-items:center;gap:6px;display:flex}.MissionSelect-module__N_AIjG__ItemName{color:#fff;font-size:14px;font-weight:600}.MissionSelect-module__N_AIjG__ItemTypes{gap:3px;display:flex}.MissionSelect-module__N_AIjG__ItemType{color:#fff;background:#ff9d0066;border-radius:3px;padding:2px 5px;font-size:10px;font-weight:600}.MissionSelect-module__N_AIjG__ItemType:hover{background:#ff9d00b3}.MissionSelect-module__N_AIjG__ItemMissionName{color:#ffffff80;font-size:12px}.MissionSelect-module__N_AIjG__NoResults{color:#ffffff80;text-align:center;padding:12px 8px;font-size:13px}
|
||||
.InspectorControls-module__gNRB6W__Controls{color:#fff;z-index:2;background:#00000080;border-radius:0 0 4px;justify-content:center;align-items:center;gap:20px;padding:8px 12px 8px 8px;font-size:13px;display:flex;position:fixed;top:0;left:0}.InspectorControls-module__gNRB6W__Dropdown,.InspectorControls-module__gNRB6W__Group{justify-content:center;align-items:center;gap:20px;display:flex}.InspectorControls-module__gNRB6W__CheckboxField,.InspectorControls-module__gNRB6W__LabelledButton,.InspectorControls-module__gNRB6W__Field{align-items:center;gap:6px;display:flex}.InspectorControls-module__gNRB6W__IconButton{color:#fff;cursor:pointer;background:#03529399;border:1px solid #c8c8c84d;border-color:#ffffff4d #c8c8c84d #c8c8c84d #ffffff4d;border-radius:4px;justify-content:center;align-items:center;width:28px;height:28px;margin:0 0 0 -12px;padding:0;font-size:15px;transition:background .2s,border-color .2s;display:flex;position:relative;transform:translate(0);box-shadow:0 1px 2px #0006}.InspectorControls-module__gNRB6W__IconButton svg{pointer-events:none}@media (hover:hover){.InspectorControls-module__gNRB6W__IconButton:hover{background:#0062b3cc;border-color:#fff6}}.InspectorControls-module__gNRB6W__IconButton:active,.InspectorControls-module__gNRB6W__IconButton[aria-expanded=true]{background:#0062b3b3;border-color:#ffffff4d;transform:translateY(1px)}.InspectorControls-module__gNRB6W__IconButton[data-active=true]{background:#0075d5e6;border-color:#fff6}.InspectorControls-module__gNRB6W__ButtonLabel{font-size:12px}.InspectorControls-module__gNRB6W__Toggle{margin:0;}.InspectorControls-module__gNRB6W__MapInfoButton{}@media (max-width:1279px){.InspectorControls-module__gNRB6W__Dropdown[data-open=false]{display:none}.InspectorControls-module__gNRB6W__Dropdown{background:#000c;border:1px solid #fff3;border-radius:4px;flex-direction:column;align-items:center;gap:12px;max-height:calc(100dvh - 56px);padding:12px;display:flex;position:absolute;top:calc(100% + 2px);left:2px;right:2px;overflow:auto;box-shadow:0 0 12px #0006}.InspectorControls-module__gNRB6W__Group{flex-wrap:wrap;gap:12px 20px}.InspectorControls-module__gNRB6W__LabelledButton{width:auto;padding:0 10px}}@media (max-width:639px){.InspectorControls-module__gNRB6W__Controls{border-radius:0;right:0}.InspectorControls-module__gNRB6W__MissionSelectWrapper{flex:1 1 0;min-width:0}.InspectorControls-module__gNRB6W__MissionSelectWrapper input{width:100%}.InspectorControls-module__gNRB6W__Toggle{flex:none}}@media (min-width:1280px){.InspectorControls-module__gNRB6W__Toggle,.InspectorControls-module__gNRB6W__LabelledButton .InspectorControls-module__gNRB6W__ButtonLabel,.InspectorControls-module__gNRB6W__MapInfoButton{display:none}}
|
||||
.InspectorControls-module__gNRB6W__Controls{color:#fff;z-index:2;background:#00000080;border-radius:0 0 4px;justify-content:center;align-items:center;gap:20px;padding:8px 12px 8px 8px;font-size:13px;display:flex;position:fixed;top:0;left:0}.InspectorControls-module__gNRB6W__Dropdown,.InspectorControls-module__gNRB6W__Group{justify-content:center;align-items:center;gap:20px;display:flex}.InspectorControls-module__gNRB6W__CheckboxField,.InspectorControls-module__gNRB6W__LabelledButton,.InspectorControls-module__gNRB6W__Field{align-items:center;gap:6px;display:flex}.InspectorControls-module__gNRB6W__IconButton{color:#fff;cursor:pointer;background:#03529399;border:1px solid #c8c8c84d;border-color:#ffffff4d #c8c8c84d #c8c8c84d #ffffff4d;border-radius:4px;justify-content:center;align-items:center;min-width:28px;height:28px;margin:0 0 0 -12px;padding:0;font-size:15px;transition:background .2s,border-color .2s;display:flex;position:relative;transform:translate(0);box-shadow:0 1px 2px #0006}.InspectorControls-module__gNRB6W__IconButton svg{pointer-events:none}@media (hover:hover){.InspectorControls-module__gNRB6W__IconButton:hover{background:#0062b3cc;border-color:#fff6}}.InspectorControls-module__gNRB6W__IconButton:active,.InspectorControls-module__gNRB6W__IconButton[aria-expanded=true]{background:#0062b3b3;border-color:#ffffff4d;transform:translateY(1px)}.InspectorControls-module__gNRB6W__IconButton[data-active=true]{background:#0075d5e6;border-color:#fff6}.InspectorControls-module__gNRB6W__ButtonLabel{font-size:12px}.InspectorControls-module__gNRB6W__Toggle{margin:0;}.InspectorControls-module__gNRB6W__MapInfoButton{}@media (max-width:1279px){.InspectorControls-module__gNRB6W__Dropdown[data-open=false]{display:none}.InspectorControls-module__gNRB6W__Dropdown{background:#000c;border:1px solid #fff3;border-radius:4px;flex-direction:column;align-items:center;gap:12px;max-height:calc(100dvh - 56px);padding:12px;display:flex;position:absolute;top:calc(100% + 2px);left:2px;right:2px;overflow:auto;box-shadow:0 0 12px #0006}.InspectorControls-module__gNRB6W__Group{flex-wrap:wrap;gap:12px 20px}.InspectorControls-module__gNRB6W__LabelledButton{width:auto;padding:0 10px}}@media (max-width:639px){.InspectorControls-module__gNRB6W__Controls{border-radius:0;right:0}.InspectorControls-module__gNRB6W__MissionSelectWrapper{flex:1 1 0;min-width:0}.InspectorControls-module__gNRB6W__MissionSelectWrapper input{width:100%}.InspectorControls-module__gNRB6W__Toggle{flex:none}}@media (min-width:1280px){.InspectorControls-module__gNRB6W__Toggle,.InspectorControls-module__gNRB6W__LabelledButton .InspectorControls-module__gNRB6W__ButtonLabel,.InspectorControls-module__gNRB6W__MapInfoButton{display:none}}
|
||||
.CopyCoordinatesButton-module__BxovtG__Root{}.CopyCoordinatesButton-module__BxovtG__Root[data-copied=true]{background:#0075d5e6;border-color:#fff6}.CopyCoordinatesButton-module__BxovtG__ClipboardCheck{opacity:1;display:none}.CopyCoordinatesButton-module__BxovtG__Root[data-copied=true] .CopyCoordinatesButton-module__BxovtG__ClipboardCheck{animation:.22s linear infinite CopyCoordinatesButton-module__BxovtG__showClipboardCheck;display:block}.CopyCoordinatesButton-module__BxovtG__Root[data-copied=true] .CopyCoordinatesButton-module__BxovtG__MapPin{display:none}.CopyCoordinatesButton-module__BxovtG__ButtonLabel{}@keyframes CopyCoordinatesButton-module__BxovtG__showClipboardCheck{0%{opacity:1}to{opacity:.2}}
|
||||
.LoadDemoButton-module__kGZaoW__Root{}.LoadDemoButton-module__kGZaoW__ButtonLabel{}.LoadDemoButton-module__kGZaoW__DemoIcon{font-size:19px}
|
||||
.JoinServerButton-module__DIR70a__Root{padding:0 5px;}.JoinServerButton-module__DIR70a__TextLabel{}.JoinServerButton-module__DIR70a__PingLabel{margin-right:2px;display:flex!important;}.JoinServerButton-module__DIR70a__LiveIcon{font-size:15px}.JoinServerButton-module__DIR70a__Pulsing{animation:1.2s ease-out infinite JoinServerButton-module__DIR70a__blink}@keyframes JoinServerButton-module__DIR70a__blink{0%{opacity:1}to{opacity:.25}}
|
||||
.DebugElements-module__Cmeo9W__StatsPanel{bottom:0;right:0;top:auto!important;left:auto!important}.DebugElements-module__Cmeo9W__AxisLabel{pointer-events:none;font-size:12px}.DebugElements-module__Cmeo9W__AxisLabel[data-axis=x]{color:#f90}.DebugElements-module__Cmeo9W__AxisLabel[data-axis=y]{color:#9f0}.DebugElements-module__Cmeo9W__AxisLabel[data-axis=z]{color:#09f}
|
||||
.FloatingLabel-module__8y09Ka__Label{color:#fff;white-space:nowrap;text-align:center;background:#00000080;border-radius:1px;padding:1px 3px;font-size:11px}
|
||||
.PlayerNameplate-module__zYDm0a__Root{pointer-events:none;white-space:nowrap;flex-direction:column;align-items:center;display:inline-flex}.PlayerNameplate-module__zYDm0a__Top{padding-bottom:20px;}.PlayerNameplate-module__zYDm0a__Bottom{padding-top:20px;}.PlayerNameplate-module__zYDm0a__IffArrow{width:12px;height:12px;image-rendering:pixelated;filter:drop-shadow(0 1px 2px #000000b3)}.PlayerNameplate-module__zYDm0a__Name{color:#fff;text-shadow:0 1px 3px #000000e6,0 0 1px #000000b3;font-size:11px}.PlayerNameplate-module__zYDm0a__HealthBar{background:#00000080;border:1px solid #fff3;width:60px;height:4px;margin:2px auto 0;overflow:hidden}.PlayerNameplate-module__zYDm0a__HealthFill{background:#2ecc40;height:100%}
|
||||
.FlagMarker-module__INpLba__Root{pointer-events:none;white-space:nowrap;flex-direction:column;align-items:center;gap:1px;display:inline-flex}.FlagMarker-module__INpLba__Distance{color:#fff;text-shadow:0 1px 3px #000000e6,0 0 1px #000000b3;opacity:.5;font-size:10px}.FlagMarker-module__INpLba__Icon{width:16px;height:16px;image-rendering:pixelated;opacity:.5;filter:drop-shadow(0 1px 3px #000c);-webkit-mask-image:var(--flag-icon-url);mask-image:var(--flag-icon-url);-webkit-mask-position:50%;mask-position:50%;-webkit-mask-size:contain;mask-size:contain;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-image:var(--flag-icon-url);-webkit-mask-position:50%;-webkit-mask-size:contain;-webkit-mask-repeat:no-repeat}
|
||||
.DemoControls-module__PjV4fq__Root{color:#fff;z-index:2;background:#000000b3;align-items:center;gap:10px;padding:8px 12px;font-size:13px;display:flex;position:fixed;bottom:0;left:0;right:0}.DemoControls-module__PjV4fq__PlayPause{color:#fff;cursor:pointer;background:#03529399;border:1px solid #ffffff4d;border-radius:4px;flex-shrink:0;justify-content:center;align-items:center;width:32px;height:32px;padding:0;font-size:14px;display:flex}@media (hover:hover){.DemoControls-module__PjV4fq__PlayPause:hover{background:#0062b3cc}}.DemoControls-module__PjV4fq__Time{font-variant-numeric:tabular-nums;white-space:nowrap;flex-shrink:0}.DemoControls-module__PjV4fq__Seek[type=range]{flex:1 1 0;min-width:0;max-width:none}.DemoControls-module__PjV4fq__Speed{color:#fff;background:#0009;border:1px solid #ffffff4d;border-radius:3px;flex-shrink:0;padding:2px 4px;font-size:12px}
|
||||
.PlayerHUD-module__-E1Scq__PlayerHUD{z-index:1;pointer-events:none;position:absolute;inset:0}.PlayerHUD-module__-E1Scq__TopRight{align-items:flex-start;gap:6px;display:flex;position:absolute;top:56px;right:8px}.PlayerHUD-module__-E1Scq__Compass{flex-shrink:0;width:64px;height:64px;position:relative}.PlayerHUD-module__-E1Scq__CompassRing{image-rendering:auto;width:100%;height:100%;position:absolute;top:0;left:0}.PlayerHUD-module__-E1Scq__CompassNSEW{width:100%;height:100%;image-rendering:pixelated;position:absolute;top:0;left:0}.PlayerHUD-module__-E1Scq__Bars{flex-direction:column;gap:3px;padding-top:10px;display:flex}.PlayerHUD-module__-E1Scq__BarTrack{background:#00000080;border:1px solid #ffffff26;width:120px;height:10px;overflow:hidden}.PlayerHUD-module__-E1Scq__BarFillHealth{background:#2ecc40;height:100%;transition:width .15s ease-out}.PlayerHUD-module__-E1Scq__BarFillEnergy{background:#0af;height:100%;transition:width .15s ease-out}.PlayerHUD-module__-E1Scq__WeaponHUD{flex-direction:column;gap:2px;display:flex;position:absolute;top:50%;right:8px;transform:translateY(-50%)}.PlayerHUD-module__-E1Scq__WeaponSeparator{height:6px}.PlayerHUD-module__-E1Scq__ChatWindow{background:#00323ca6;max-width:420px;padding:4px 8px;font-size:12px;line-height:1.3;position:absolute;top:56px;left:0}.PlayerHUD-module__-E1Scq__ChatMessage{color:#2cacb5;padding:1px 0;transition:opacity .3s ease-out}.PlayerHUD-module__-E1Scq__ChatColor0{color:#2cacb5}.PlayerHUD-module__-E1Scq__ChatColor1{color:#04eb69}.PlayerHUD-module__-E1Scq__ChatColor2{color:#dbc880}.PlayerHUD-module__-E1Scq__ChatColor3{color:#4dfd5f}.PlayerHUD-module__-E1Scq__ChatColor4{color:#28e7f0}.PlayerHUD-module__-E1Scq__ChatColor5{color:#c8c832}.PlayerHUD-module__-E1Scq__ChatColor6{color:#c8c8c8}.PlayerHUD-module__-E1Scq__ChatColor7{color:#dcdc14}.PlayerHUD-module__-E1Scq__ChatColor8{color:#9696fa}.PlayerHUD-module__-E1Scq__ChatColor9{color:#3cdc96}.PlayerHUD-module__-E1Scq__TeamScores{font-family:monospace;font-size:12px;position:absolute;bottom:130px;left:0}.PlayerHUD-module__-E1Scq__TeamRow{background:#00323ca6;gap:6px;padding:2px 8px;display:flex}.PlayerHUD-module__-E1Scq__TeamRow+.PlayerHUD-module__-E1Scq__TeamRow{border-top:1px solid #80ffc826}.PlayerHUD-module__-E1Scq__TeamNameFriendly{color:#2ecc40;min-width:60px}.PlayerHUD-module__-E1Scq__TeamNameEnemy{color:#e44;min-width:60px}.PlayerHUD-module__-E1Scq__TeamScore{color:#fff;text-align:right;min-width:24px;font-weight:700}.PlayerHUD-module__-E1Scq__TeamCount{color:#9ba;text-align:right;min-width:24px}.PlayerHUD-module__-E1Scq__PackInventoryHUD{align-items:center;gap:4px;display:flex;position:absolute;bottom:100px;right:8px}.PlayerHUD-module__-E1Scq__PackInvItem{background:#00323ca6;border:1px solid #80ffc826;flex-direction:column;justify-content:center;align-items:center;gap:1px;padding:4px;display:flex}.PlayerHUD-module__-E1Scq__PackInvItemActive{border-color:#80ffc880;box-shadow:0 0 6px #80ffc84d}.PlayerHUD-module__-E1Scq__PackInvItemDim{opacity:.5}.PlayerHUD-module__-E1Scq__PackInvIcon{image-rendering:pixelated;display:block}.PlayerHUD-module__-E1Scq__PackInvCount{color:#bfe;text-align:center;min-width:12px;font-family:monospace;font-size:11px}.PlayerHUD-module__-E1Scq__PackInvInfinity{image-rendering:pixelated;opacity:.8;display:block}.PlayerHUD-module__-E1Scq__Reticle{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}.PlayerHUD-module__-E1Scq__ReticleImage{opacity:.85;width:64px;height:64px;image-rendering:pixelated}.PlayerHUD-module__-E1Scq__ReticleDot{background:#2ecc40b3;border-radius:50%;width:4px;height:4px;box-shadow:0 0 4px #2ecc4080}
|
||||
.page-module__E0kJGG__CanvasContainer{z-index:0;position:absolute;inset:0}.page-module__E0kJGG__LoadingIndicator{pointer-events:none;z-index:1;opacity:.8;flex-direction:column;align-items:center;gap:16px;display:flex;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}.page-module__E0kJGG__LoadingIndicator[data-complete=true]{animation:.3s ease-out forwards page-module__E0kJGG__loadingComplete}.page-module__E0kJGG__Spinner{border:4px solid #fff3;border-top-color:#fff;border-radius:50%;width:48px;height:48px;animation:1s linear infinite page-module__E0kJGG__spin}.page-module__E0kJGG__Progress{background:#fff3;border-radius:2px;width:200px;height:4px;overflow:hidden}.page-module__E0kJGG__ProgressBar{background:#fff;border-radius:2px;height:100%;transition:width .1s ease-out}.page-module__E0kJGG__ProgressText{color:#ffffffb3;font-variant-numeric:tabular-nums;font-size:14px}@keyframes page-module__E0kJGG__spin{to{transform:rotate(360deg)}}@keyframes page-module__E0kJGG__loadingComplete{0%{opacity:1}to{opacity:0}}
|
||||
.page-module__v6zvCa__CanvasContainer{z-index:0;position:absolute;inset:0}.page-module__v6zvCa__LoadingIndicator{pointer-events:none;z-index:1;opacity:.8;flex-direction:column;align-items:center;gap:16px;display:flex;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}.page-module__v6zvCa__LoadingIndicator[data-complete=true]{animation:.3s ease-out forwards page-module__v6zvCa__loadingComplete}.page-module__v6zvCa__Spinner{border:4px solid #fff3;border-top-color:#fff;border-radius:50%;width:48px;height:48px;animation:1s linear infinite page-module__v6zvCa__spin}@keyframes page-module__v6zvCa__spin{to{transform:rotate(360deg)}}@keyframes page-module__v6zvCa__loadingComplete{0%{opacity:1}to{opacity:0}}.page-module__v6zvCa__Sidebar{-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);color:#fff;z-index:2;background:#000000b3;flex-direction:column;width:260px;font-size:13px;display:flex;position:fixed;top:0;bottom:0;left:0;overflow:hidden}.page-module__v6zvCa__SidebarSection{border-bottom:1px solid #ffffff1a;padding:10px 12px}.page-module__v6zvCa__SidebarSection:last-child{border-bottom:none}.page-module__v6zvCa__SectionLabel{text-transform:uppercase;letter-spacing:.05em;color:#fff6;margin-bottom:6px;font-size:10px}.page-module__v6zvCa__AnimationList{flex:1;padding:0 12px 12px;overflow-y:auto}.page-module__v6zvCa__AnimationItem{cursor:pointer;-webkit-user-select:none;user-select:none;border-radius:4px;align-items:center;gap:6px;padding:4px 6px;display:flex}.page-module__v6zvCa__AnimationItem:hover{background:#ffffff14}.page-module__v6zvCa__AnimationItem[data-active=true]{background:#ffffff26}.page-module__v6zvCa__PlayButton{color:#fff9;cursor:pointer;background:#ffffff1a;border:none;border-radius:4px;flex-shrink:0;justify-content:center;align-items:center;width:22px;height:22px;padding:0;font-size:11px;display:flex}.page-module__v6zvCa__PlayButton:hover{color:#fff;background:#fff3}.page-module__v6zvCa__AnimationItem[data-active=true] .page-module__v6zvCa__PlayButton{color:#fff;background:#64b4ff4d}.page-module__v6zvCa__AnimationName{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.page-module__v6zvCa__ClipName{color:#ffffff4d;white-space:nowrap;flex-shrink:0;font-size:10px}.page-module__v6zvCa__CyclicIcon{color:#ffffff4d;title:"Cyclic (looping)";flex-shrink:0;font-size:13px}.page-module__v6zvCa__CheckboxField{align-items:center;gap:6px;display:flex}
|
||||
File diff suppressed because one or more lines are too long
38
docs/_next/static/chunks/14c6376ae0b23060.js
Normal file
38
docs/_next/static/chunks/14c6376ae0b23060.js
Normal file
File diff suppressed because one or more lines are too long
174
docs/_next/static/chunks/164bc8495505bc95.js
Normal file
174
docs/_next/static/chunks/164bc8495505bc95.js
Normal file
File diff suppressed because one or more lines are too long
8
docs/_next/static/chunks/1a2c6dc513278881.js
Normal file
8
docs/_next/static/chunks/1a2c6dc513278881.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
docs/_next/static/chunks/1fe5719635cf5984.js
Normal file
1
docs/_next/static/chunks/1fe5719635cf5984.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
docs/_next/static/chunks/2400be5b6a2e4806.js
Normal file
1
docs/_next/static/chunks/2400be5b6a2e4806.js
Normal file
File diff suppressed because one or more lines are too long
1
docs/_next/static/chunks/2677e6a5750bb60c.js
Normal file
1
docs/_next/static/chunks/2677e6a5750bb60c.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
docs/_next/static/chunks/2edeeda5ca6dc680.js
Normal file
1
docs/_next/static/chunks/2edeeda5ca6dc680.js
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -1 +0,0 @@
|
|||
.TouchControls-module__AkxfgW__Joystick{z-index:1;width:140px;height:140px;position:fixed;bottom:20px;left:50%;transform:translate(-50%)}.TouchControls-module__AkxfgW__Left{left:20px;transform:none;}.TouchControls-module__AkxfgW__Right{left:auto;right:20px;transform:none;}
|
||||
File diff suppressed because one or more lines are too long
1
docs/_next/static/chunks/49bf5eb2ca42014f.js
Normal file
1
docs/_next/static/chunks/49bf5eb2ca42014f.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
17
docs/_next/static/chunks/576b06837c0cb7a0.js
Normal file
17
docs/_next/static/chunks/576b06837c0cb7a0.js
Normal file
File diff suppressed because one or more lines are too long
397
docs/_next/static/chunks/629d98e413c0344a.js
Normal file
397
docs/_next/static/chunks/629d98e413c0344a.js
Normal file
File diff suppressed because one or more lines are too long
1
docs/_next/static/chunks/687860f86433eb04.js
Normal file
1
docs/_next/static/chunks/687860f86433eb04.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,29055,e=>{"use strict";var t=e.i(43476),r=e.i(71645),n=e.i(15080),o=e.i(71753),i=e.i(90072),u=e.i(12979),c=e.i(51434),a=e.i(79123),s=e.i(89887);e.i(13876);var l=e.i(58647);let f=new Map,d=new Map;function p(e,t=1){d.set(e,t)}function m(e){d.delete(e)}let y=0;function h(){return y}function D(){for(let[e]of(y++,d)){try{e.stop()}catch{}try{e.disconnect()}catch{}}d.clear()}function g(e,t){let r=t(e),n=r?.filename;if(!n)return null;let o=n.endsWith(".wav")?n:`${n}.wav`,i=r.description,u=null!=i?t(i):void 0,c=u?.is3D??!0,a=u?.isLooping??!1,s=u?.referenceDistance??20;return{filename:o,is3D:c,isLooping:a,refDist:s,maxDist:u?.maxDistance??100,volume:u?.volume??1}}function v(e,t,r,n,o){let c;try{c=(0,u.audioToUrl)(e.filename)}catch{return}let a=l.engineStore.getState().playback.rate,s=y;R(c,r,r=>{if(s===y)try{if(e.is3D&&o){let u=new i.PositionalAudio(t);u.setBuffer(r),u.setDistanceModel("inverse"),u.setRefDistance(e.refDist),u.setMaxDistance(e.maxDist),u.setRolloffFactor(1),u.setVolume(e.volume),u.setPlaybackRate(a),n&&u.position.copy(n),o.add(u),d.set(u,1),u.play(),u.source.onended=()=>{d.delete(u);try{u.disconnect()}catch{}o.remove(u)}}else{let n=new i.Audio(t);n.setBuffer(r),n.setVolume(e.volume),n.setPlaybackRate(a),d.set(n,1),n.play(),n.source.onended=()=>{d.delete(n);try{n.disconnect()}catch{}}}}catch{}})}function R(e,t,r){f.has(e)?r(f.get(e)):t.load(e,t=>{f.set(e,t),r(t)},void 0,t=>{console.error("Audio load error",e,t)})}l.engineStore.subscribe(e=>e.playback.rate,e=>{for(let[t,r]of d)try{t.setPlaybackRate(r*e)}catch{}});let M=(0,r.memo)(function({entity:e}){let{debugMode:l}=(0,a.useDebug)(),f=e.audioFileName??"",d=e.audioVolume??1,p=e.audioMinDistance??1,m=e.audioMaxDistance??1,y=e.audioMinLoopGap??0,h=e.audioMaxLoopGap??0,D=e.audioIs3D??!0?1:0,g=e.audioIsLooping??!0,[v,M,T]=e.position??[0,0,0],{scene:b,camera:x}=(0,n.useThree)(),{audioLoader:A,audioListener:S}=(0,c.useAudio)(),{audioEnabled:w}=(0,a.useSettings)(),P=(0,r.useRef)(null),k=(0,r.useRef)(null),B=(0,r.useRef)(null),L=(0,r.useRef)(!1),V=(0,r.useRef)(!1),j=(0,r.useRef)(new i.Vector3(v,M,T)),F=(0,r.useRef)(0),C=()=>{null!=k.current&&(clearTimeout(k.current),k.current=null),null!=B.current&&(clearTimeout(B.current),B.current=null)};(0,r.useEffect)(()=>{let e;if(A&&S){if(F.current++,D){let t=new i.PositionalAudio(S);t.position.copy(j.current),t.setDistanceModel("inverse"),t.setRefDistance(p),t.setMaxDistance(m),t.setRolloffFactor(1),t.setVolume(d),e=t,b.add(e)}else(e=new i.Audio(S)).setVolume(d);return P.current=e,()=>{C();try{e.stop()}catch{}try{e.disconnect()}catch{}D&&b.remove(e),P.current=null,L.current=!1,V.current=!1}}},[A,S,D,p,m,d,b]);let E=(e,t)=>{if(g)if(y>0||h>0){let r=Math.max(0,y),n=Math.max(r,h),o=r===n?r:Math.random()*(n-r)+r;e.loop=!1;let i=()=>{t===F.current&&(!1===e.isPlaying?k.current=setTimeout(()=>{if(t===F.current)try{e.play(),E(e,t)}catch{}},o):B.current=setTimeout(i,100))};B.current=setTimeout(i,100)}else e.setLoop(!0)},G=e=>{if(!A)return;let t=F.current;if(L.current)try{e.isPlaying||(e.play(),E(e,t))}catch{}else{let r;try{r=(0,u.audioToUrl)(f)}catch{return}R(r,A,r=>{if(t===F.current&&!e.buffer){e.setBuffer(r),L.current=!0;try{e.play(),E(e,t)}catch{}}})}};return(0,r.useEffect)(()=>{let e=P.current;e&&!D&&w&&f&&G(e)},[w,D,f,A,S]),(0,o.useFrame)(()=>{let e=P.current;if(!e||!D||!w||!f)return;let t=x.position.distanceTo(j.current),r=V.current,n=t<=m;if(n&&!r)V.current=!0,G(e);else if(!n&&r){V.current=!1,C();try{e.stop()}catch{}}}),(0,r.useEffect)(()=>{let e=P.current;if(e&&!w){C();try{e.stop()}catch{}V.current=!1}},[w]),l?(0,t.jsxs)("mesh",{position:j.current,children:[(0,t.jsx)("sphereGeometry",{args:[p,12,12]}),(0,t.jsx)("meshBasicMaterial",{color:"#00ff00",wireframe:!0,opacity:.05,transparent:!0,toneMapped:!1}),(0,t.jsx)(s.FloatingLabel,{color:"#00ff00",position:[0,p+1,0],children:f})]}):null});e.s(["AudioEmitter",0,M,"audioBufferCache",0,f,"getCachedAudioBuffer",()=>R,"getSoundGeneration",()=>h,"playOneShotSound",()=>v,"resolveAudioProfile",()=>g,"stopAllTrackedSounds",()=>D,"trackSound",()=>p,"untrackSound",()=>m])}]);
|
||||
75
docs/_next/static/chunks/6f1c1038c8b3b80c.js
Normal file
75
docs/_next/static/chunks/6f1c1038c8b3b80c.js
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,63724,e=>{"use strict";var r=e.i(43476),o=e.i(932),a=e.i(71645),t=e.i(47071),l=e.i(71753),c=e.i(90072),n=e.i(12979),i=e.i(79123);let u=`
|
||||
#include <fog_pars_vertex>
|
||||
|
||||
varying vec2 vUv;
|
||||
|
||||
void main() {
|
||||
vUv = uv;
|
||||
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
|
||||
gl_Position = projectionMatrix * mvPosition;
|
||||
#include <fog_vertex>
|
||||
}
|
||||
`,s=`
|
||||
#include <fog_pars_fragment>
|
||||
|
||||
uniform sampler2D frame0;
|
||||
uniform sampler2D frame1;
|
||||
uniform sampler2D frame2;
|
||||
uniform sampler2D frame3;
|
||||
uniform sampler2D frame4;
|
||||
uniform int currentFrame;
|
||||
uniform float vScroll;
|
||||
uniform vec2 uvScale;
|
||||
uniform vec3 tintColor;
|
||||
uniform float opacity;
|
||||
uniform float opacityFactor;
|
||||
|
||||
varying vec2 vUv;
|
||||
|
||||
void main() {
|
||||
// Scale and scroll UVs
|
||||
vec2 scrolledUv = vec2(vUv.x * uvScale.x, vUv.y * uvScale.y + vScroll);
|
||||
|
||||
// Sample the current frame
|
||||
vec4 texColor;
|
||||
if (currentFrame == 0) {
|
||||
texColor = texture2D(frame0, scrolledUv);
|
||||
} else if (currentFrame == 1) {
|
||||
texColor = texture2D(frame1, scrolledUv);
|
||||
} else if (currentFrame == 2) {
|
||||
texColor = texture2D(frame2, scrolledUv);
|
||||
} else if (currentFrame == 3) {
|
||||
texColor = texture2D(frame3, scrolledUv);
|
||||
} else {
|
||||
texColor = texture2D(frame4, scrolledUv);
|
||||
}
|
||||
|
||||
// Tribes 2 GL_MODULATE: output = texture * vertexColor
|
||||
// No gamma correction - textures use NoColorSpace and values pass through
|
||||
// directly to display, matching how WaterBlock handles sRGB textures.
|
||||
vec3 modulatedColor = texColor.rgb * tintColor;
|
||||
|
||||
float adjustedOpacity = opacity * opacityFactor;
|
||||
|
||||
gl_FragColor = vec4(modulatedColor, adjustedOpacity);
|
||||
|
||||
// Custom fog for additive blending: fade out rather than blend to fog color.
|
||||
// Standard fog (mix toward fogColor) doesn't work with additive blending
|
||||
// because we'd still be adding fogColor to the framebuffer.
|
||||
// Uses Torque's quadratic haze formula for consistency.
|
||||
#ifdef USE_FOG
|
||||
float dist = vFogDepth;
|
||||
float fogFactor = 0.0;
|
||||
if (dist > fogNear) {
|
||||
if (dist >= fogFar) {
|
||||
fogFactor = 1.0;
|
||||
} else {
|
||||
float fogScale = 1.0 / (fogFar - fogNear);
|
||||
float distFactor = (dist - fogNear) * fogScale - 1.0;
|
||||
fogFactor = 1.0 - distFactor * distFactor;
|
||||
}
|
||||
}
|
||||
gl_FragColor.a *= 1.0 - fogFactor;
|
||||
#endif
|
||||
}
|
||||
`;function f(e){let r,t,l,n=(0,o.c)(7),[i,u,s]=e;n[0]!==i||n[1]!==u||n[2]!==s?((r=new c.BoxGeometry(i,u,s)).translate(i/2,u/2,s/2),n[0]=i,n[1]=u,n[2]=s,n[3]=r):r=n[3];let f=r;return n[4]!==f?(t=()=>()=>f.dispose(),l=[f],n[4]=f,n[5]=t,n[6]=l):(t=n[5],l=n[6]),(0,a.useEffect)(t,l),f}function m(e){let a,t,l,n=(0,o.c)(10),{scale:i,color:u,baseTranslucency:s}=e,m=f(i);n[0]!==u[0]||n[1]!==u[1]||n[2]!==u[2]?(a=new c.Color(u[0],u[1],u[2]),n[0]=u[0],n[1]=u[1],n[2]=u[2],n[3]=a):a=n[3];let d=a,v=+s;return n[4]!==d||n[5]!==v?(t=(0,r.jsx)("meshBasicMaterial",{color:d,transparent:!0,opacity:v,blending:c.AdditiveBlending,side:c.DoubleSide,depthWrite:!1,fog:!1}),n[4]=d,n[5]=v,n[6]=t):t=n[6],n[7]!==m||n[8]!==t?(l=(0,r.jsx)("mesh",{geometry:m,renderOrder:1,children:t}),n[7]=m,n[8]=t,n[9]=l):l=n[9],l}function d({scale:e,data:o}){let{animationEnabled:m}=(0,i.useSettings)(),d=f(e),v=(0,a.useMemo)(()=>o.textures.map(e=>(0,n.textureToUrl)(e)),[o.textures]),g=(0,t.useTexture)(v,e=>{e.forEach(e=>{e.wrapS=e.wrapT=c.RepeatWrapping,e.colorSpace=c.NoColorSpace,e.flipY=!1,e.needsUpdate=!0})}),p=(0,a.useMemo)(()=>(function({textures:e,scale:r,umapping:o,vmapping:a,color:t,baseTranslucency:l}){let n=[...r].sort((e,r)=>r-e),i=new c.Vector2(n[0]*o,n[1]*a),f=e[0];return new c.ShaderMaterial({uniforms:{frame0:{value:f},frame1:{value:e[1]??f},frame2:{value:e[2]??f},frame3:{value:e[3]??f},frame4:{value:e[4]??f},currentFrame:{value:0},vScroll:{value:0},uvScale:{value:i},tintColor:{value:new c.Color(...t)},opacity:{value:l},opacityFactor:{value:1},fogColor:{value:new c.Color},fogNear:{value:1},fogFar:{value:2e3}},vertexShader:u,fragmentShader:s,transparent:!0,blending:c.AdditiveBlending,side:c.DoubleSide,depthWrite:!1,fog:!0})})({textures:g,scale:e,umapping:o.umapping,vmapping:o.vmapping,color:o.color,baseTranslucency:o.baseTranslucency}),[g,e,o]);(0,a.useEffect)(()=>()=>p.dispose(),[p]);let x=(0,a.useRef)(0);return(0,l.useFrame)((e,r)=>{if(!m){x.current=0,p.uniforms.currentFrame.value=0,p.uniforms.vScroll.value=0;return}x.current+=r,p.uniforms.currentFrame.value=Math.floor(x.current*o.framesPerSec)%o.numFrames,p.uniforms.vScroll.value=x.current*o.scrollSpeed}),(0,r.jsx)("mesh",{geometry:d,material:p,renderOrder:1})}function v(e){let t,l,c,n=(0,o.c)(14),{data:i,scale:u}=e;if(0===i.textures.map(g).length){let e;return n[0]!==i.baseTranslucency||n[1]!==i.color||n[2]!==u?(e=(0,r.jsx)(m,{scale:u,color:i.color,baseTranslucency:i.baseTranslucency}),n[0]=i.baseTranslucency,n[1]=i.color,n[2]=u,n[3]=e):e=n[3],e}return n[4]!==i.baseTranslucency||n[5]!==i.color||n[6]!==u?(t=(0,r.jsx)(m,{scale:u,color:i.color,baseTranslucency:i.baseTranslucency}),n[4]=i.baseTranslucency,n[5]=i.color,n[6]=u,n[7]=t):t=n[7],n[8]!==i||n[9]!==u?(l=(0,r.jsx)(d,{scale:u,data:i}),n[8]=i,n[9]=u,n[10]=l):l=n[10],n[11]!==t||n[12]!==l?(c=(0,r.jsx)(a.Suspense,{fallback:t,children:l}),n[11]=t,n[12]=l,n[13]=c):c=n[13],c}function g(e){return(0,n.textureToUrl)(e)}e.s(["ForceFieldBare",()=>v],63724)}]);
|
||||
1
docs/_next/static/chunks/718b5c36c0eed37e.js
Normal file
1
docs/_next/static/chunks/718b5c36c0eed37e.js
Normal file
File diff suppressed because one or more lines are too long
166
docs/_next/static/chunks/796073b9f885dcbc.js
Normal file
166
docs/_next/static/chunks/796073b9f885dcbc.js
Normal file
File diff suppressed because one or more lines are too long
174
docs/_next/static/chunks/818dce712bc45cb4.js
Normal file
174
docs/_next/static/chunks/818dce712bc45cb4.js
Normal file
File diff suppressed because one or more lines are too long
1
docs/_next/static/chunks/88a5cf0ea7f16af4.js
Normal file
1
docs/_next/static/chunks/88a5cf0ea7f16af4.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,85413,e=>{"use strict";var t=Object.defineProperty;class i{constructor(){((e,i,s)=>{let r,n;n=void 0,(r="symbol"!=typeof i?i+"":i)in e?t(e,r,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[r]=n})(this,"_listeners")}addEventListener(e,t){void 0===this._listeners&&(this._listeners={});let i=this._listeners;void 0===i[e]&&(i[e]=[]),-1===i[e].indexOf(t)&&i[e].push(t)}hasEventListener(e,t){if(void 0===this._listeners)return!1;let i=this._listeners;return void 0!==i[e]&&-1!==i[e].indexOf(t)}removeEventListener(e,t){if(void 0===this._listeners)return;let i=this._listeners[e];if(void 0!==i){let e=i.indexOf(t);-1!==e&&i.splice(e,1)}}dispatchEvent(e){if(void 0===this._listeners)return;let t=this._listeners[e.type];if(void 0!==t){e.target=this;let i=t.slice(0);for(let t=0,s=i.length;t<s;t++)i[t].call(this,e);e.target=null}}}e.s(["EventDispatcher",()=>i])}]);
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,63724,e=>{"use strict";var r=e.i(43476),t=e.i(932),o=e.i(71645),a=e.i(47071),l=e.i(71753),i=e.i(90072),n=e.i(62395),s=e.i(12979),c=e.i(79123),u=e.i(6112);let f=`
|
||||
#include <fog_pars_vertex>
|
||||
|
||||
varying vec2 vUv;
|
||||
|
||||
void main() {
|
||||
vUv = uv;
|
||||
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
|
||||
gl_Position = projectionMatrix * mvPosition;
|
||||
#include <fog_vertex>
|
||||
}
|
||||
`,m=`
|
||||
#include <fog_pars_fragment>
|
||||
|
||||
uniform sampler2D frame0;
|
||||
uniform sampler2D frame1;
|
||||
uniform sampler2D frame2;
|
||||
uniform sampler2D frame3;
|
||||
uniform sampler2D frame4;
|
||||
uniform int currentFrame;
|
||||
uniform float vScroll;
|
||||
uniform vec2 uvScale;
|
||||
uniform vec3 tintColor;
|
||||
uniform float opacity;
|
||||
uniform float opacityFactor;
|
||||
|
||||
varying vec2 vUv;
|
||||
|
||||
void main() {
|
||||
// Scale and scroll UVs
|
||||
vec2 scrolledUv = vec2(vUv.x * uvScale.x, vUv.y * uvScale.y + vScroll);
|
||||
|
||||
// Sample the current frame
|
||||
vec4 texColor;
|
||||
if (currentFrame == 0) {
|
||||
texColor = texture2D(frame0, scrolledUv);
|
||||
} else if (currentFrame == 1) {
|
||||
texColor = texture2D(frame1, scrolledUv);
|
||||
} else if (currentFrame == 2) {
|
||||
texColor = texture2D(frame2, scrolledUv);
|
||||
} else if (currentFrame == 3) {
|
||||
texColor = texture2D(frame3, scrolledUv);
|
||||
} else {
|
||||
texColor = texture2D(frame4, scrolledUv);
|
||||
}
|
||||
|
||||
// Tribes 2 GL_MODULATE: output = texture * vertexColor
|
||||
// No gamma correction - textures use NoColorSpace and values pass through
|
||||
// directly to display, matching how WaterBlock handles sRGB textures.
|
||||
vec3 modulatedColor = texColor.rgb * tintColor;
|
||||
|
||||
float adjustedOpacity = opacity * opacityFactor;
|
||||
|
||||
gl_FragColor = vec4(modulatedColor, adjustedOpacity);
|
||||
|
||||
// Custom fog for additive blending: fade out rather than blend to fog color.
|
||||
// Standard fog (mix toward fogColor) doesn't work with additive blending
|
||||
// because we'd still be adding fogColor to the framebuffer.
|
||||
// Uses Torque's quadratic haze formula for consistency.
|
||||
#ifdef USE_FOG
|
||||
float dist = vFogDepth;
|
||||
float fogFactor = 0.0;
|
||||
if (dist > fogNear) {
|
||||
if (dist >= fogFar) {
|
||||
fogFactor = 1.0;
|
||||
} else {
|
||||
float fogScale = 1.0 / (fogFar - fogNear);
|
||||
float distFactor = (dist - fogNear) * fogScale - 1.0;
|
||||
fogFactor = 1.0 - distFactor * distFactor;
|
||||
}
|
||||
}
|
||||
gl_FragColor.a *= 1.0 - fogFactor;
|
||||
#endif
|
||||
}
|
||||
`;function d(e){let r,a,l,n=(0,t.c)(7),[s,c,u]=e;n[0]!==s||n[1]!==c||n[2]!==u?((r=new i.BoxGeometry(s,c,u)).translate(s/2,c/2,u/2),n[0]=s,n[1]=c,n[2]=u,n[3]=r):r=n[3];let f=r;return n[4]!==f?(a=()=>()=>f.dispose(),l=[f],n[4]=f,n[5]=a,n[6]=l):(a=n[5],l=n[6]),(0,o.useEffect)(a,l),f}function p({scale:e,color:t,baseTranslucency:n,textureUrls:s,numFrames:u,framesPerSec:p,scrollSpeed:v,umapping:g,vmapping:x}){let{animationEnabled:F}=(0,c.useSettings)(),y=d(e),S=(0,a.useTexture)(s,e=>{e.forEach(e=>{e.wrapS=e.wrapT=i.RepeatWrapping,e.colorSpace=i.NoColorSpace,e.flipY=!1,e.needsUpdate=!0})}),h=(0,o.useMemo)(()=>(function({textures:e,scale:r,umapping:t,vmapping:o,color:a,baseTranslucency:l}){let n=[...r].sort((e,r)=>r-e),s=new i.Vector2(n[0]*t,n[1]*o),c=e[0];return new i.ShaderMaterial({uniforms:{frame0:{value:c},frame1:{value:e[1]??c},frame2:{value:e[2]??c},frame3:{value:e[3]??c},frame4:{value:e[4]??c},currentFrame:{value:0},vScroll:{value:0},uvScale:{value:s},tintColor:{value:new i.Color(...a)},opacity:{value:l},opacityFactor:{value:1},fogColor:{value:new i.Color},fogNear:{value:1},fogFar:{value:2e3}},vertexShader:f,fragmentShader:m,transparent:!0,blending:i.AdditiveBlending,side:i.DoubleSide,depthWrite:!1,fog:!0})})({textures:S,scale:e,umapping:g,vmapping:x,color:t,baseTranslucency:n}),[S,e,g,x,t,n]);(0,o.useEffect)(()=>()=>h.dispose(),[h]);let C=(0,o.useRef)(0);return(0,l.useFrame)((e,r)=>{if(!F){C.current=0,h.uniforms.currentFrame.value=0,h.uniforms.vScroll.value=0;return}C.current+=r,h.uniforms.currentFrame.value=Math.floor(C.current*p)%u,h.uniforms.vScroll.value=C.current*v}),(0,r.jsx)("mesh",{geometry:y,material:h,renderOrder:1})}function v(e){let o,a,l,n=(0,t.c)(10),{scale:s,color:c,baseTranslucency:u}=e,f=d(s);n[0]!==c[0]||n[1]!==c[1]||n[2]!==c[2]?(o=new i.Color(c[0],c[1],c[2]),n[0]=c[0],n[1]=c[1],n[2]=c[2],n[3]=o):o=n[3];let m=o,p=+u;return n[4]!==m||n[5]!==p?(a=(0,r.jsx)("meshBasicMaterial",{color:m,transparent:!0,opacity:p,blending:i.AdditiveBlending,side:i.DoubleSide,depthWrite:!1,fog:!1}),n[4]=m,n[5]=p,n[6]=a):a=n[6],n[7]!==f||n[8]!==a?(l=(0,r.jsx)("mesh",{geometry:f,renderOrder:1,children:a}),n[7]=f,n[8]=a,n[9]=l):l=n[9],l}let g=(0,o.memo)(function(e){let a,l,i,c,f,m,d,g,x,F,y,S,h,C,b,U,P,D=(0,t.c)(56),{object:w}=e;D[0]!==w?(a=(0,n.getPosition)(w),D[0]=w,D[1]=a):a=D[1];let T=a;D[2]!==w?(l=(0,n.getRotation)(w),D[2]=w,D[3]=l):l=D[3];let j=l;D[4]!==w?(i=(0,n.getScale)(w),D[4]=w,D[5]=i):i=D[5];let B=i;D[6]!==w?(c=(0,n.getProperty)(w,"dataBlock"),D[6]=w,D[7]=c):c=D[7];let _=(0,u.useDatablock)(c);D[8]!==_?(f=(0,n.getProperty)(_,"color"),D[8]=_,D[9]=f):f=D[9];let O=f;if(D[10]!==O){let e;m=O?[(e=O.split(" ").map(e=>parseFloat(e)))[0]??0,e[1]??0,e[2]??0]:[1,1,1],D[10]=O,D[11]=m}else m=D[11];let M=m;D[12]!==_?(d=parseFloat((0,n.getProperty)(_,"baseTranslucency"))||1,D[12]=_,D[13]=d):d=D[13];let N=d;D[14]!==_?(g=parseInt((0,n.getProperty)(_,"numFrames"),10)||1,D[14]=_,D[15]=g):g=D[15];let R=g;D[16]!==_?(x=parseFloat((0,n.getProperty)(_,"framesPerSec"))||1,D[16]=_,D[17]=x):x=D[17];let k=x;D[18]!==_?(F=parseFloat((0,n.getProperty)(_,"scrollSpeed"))||0,D[18]=_,D[19]=F):F=D[19];let A=F;D[20]!==_?(y=parseFloat((0,n.getProperty)(_,"umapping"))||1,D[20]=_,D[21]=y):y=D[21];let E=y;D[22]!==_?(S=parseFloat((0,n.getProperty)(_,"vmapping"))||1,D[22]=_,D[23]=S):S=D[23];let q=S;D[24]!==_||D[25]!==R?(h=function(e,r){let t=[];for(let o=0;o<r;o++){let r=(0,n.getProperty)(e,`texture${o}`);r&&t.push((0,s.textureToUrl)(r))}return t}(_,R),D[24]=_,D[25]=R,D[26]=h):h=D[26];let G=h;if(0===G.length){let e,t;return D[27]!==N||D[28]!==M||D[29]!==B?(e=(0,r.jsx)(v,{scale:B,color:M,baseTranslucency:N}),D[27]=N,D[28]=M,D[29]=B,D[30]=e):e=D[30],D[31]!==T||D[32]!==j||D[33]!==e?(t=(0,r.jsx)("group",{position:T,quaternion:j,children:e}),D[31]=T,D[32]=j,D[33]=e,D[34]=t):t=D[34],t}return D[35]!==N||D[36]!==M||D[37]!==B?(C=(0,r.jsx)(v,{scale:B,color:M,baseTranslucency:N}),D[35]=N,D[36]=M,D[37]=B,D[38]=C):C=D[38],D[39]!==N||D[40]!==M||D[41]!==k||D[42]!==R||D[43]!==B||D[44]!==A||D[45]!==G||D[46]!==E||D[47]!==q?(b=(0,r.jsx)(p,{scale:B,color:M,baseTranslucency:N,textureUrls:G,numFrames:R,framesPerSec:k,scrollSpeed:A,umapping:E,vmapping:q}),D[39]=N,D[40]=M,D[41]=k,D[42]=R,D[43]=B,D[44]=A,D[45]=G,D[46]=E,D[47]=q,D[48]=b):b=D[48],D[49]!==C||D[50]!==b?(U=(0,r.jsx)(o.Suspense,{fallback:C,children:b}),D[49]=C,D[50]=b,D[51]=U):U=D[51],D[52]!==T||D[53]!==j||D[54]!==U?(P=(0,r.jsx)("group",{position:T,quaternion:j,children:U}),D[52]=T,D[53]=j,D[54]=U,D[55]=P):P=D[55],P});e.s(["ForceFieldBare",0,g],63724)}]);
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
docs/_next/static/chunks/99bc9f3ae93187f1.css
Normal file
1
docs/_next/static/chunks/99bc9f3ae93187f1.css
Normal file
|
|
@ -0,0 +1 @@
|
|||
.DemoPlaybackControls-module__A_AHSq__Root{color:#fff;z-index:2;background:#000000b3;align-items:center;gap:10px;padding:8px 12px;font-size:13px;display:flex;position:fixed;bottom:0;left:0;right:0}.DemoPlaybackControls-module__A_AHSq__PlayPause{color:#fff;cursor:pointer;background:#03529399;border:1px solid #ffffff4d;border-radius:4px;flex-shrink:0;justify-content:center;align-items:center;width:32px;height:32px;padding:0;font-size:14px;display:flex}@media (hover:hover){.DemoPlaybackControls-module__A_AHSq__PlayPause:hover{background:#0062b3cc}}.DemoPlaybackControls-module__A_AHSq__Time{font-variant-numeric:tabular-nums;white-space:nowrap;flex-shrink:0}.DemoPlaybackControls-module__A_AHSq__Seek[type=range]{flex:1 1 0;min-width:0;max-width:none}.DemoPlaybackControls-module__A_AHSq__Speed{color:#fff;background:#0009;border:1px solid #ffffff4d;border-radius:3px;flex-shrink:0;padding:2px 4px;font-size:12px}
|
||||
1
docs/_next/static/chunks/9eaea0ae086bad69.js
Normal file
1
docs/_next/static/chunks/9eaea0ae086bad69.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,11152,40141,t=>{"use strict";var e=t.i(71645),r={color:void 0,size:void 0,className:void 0,style:void 0,attr:void 0},o=e.default.createContext&&e.default.createContext(r),n=["attr","size","title"];function c(){return(c=Object.assign.bind()).apply(this,arguments)}function l(t,e){var r=Object.keys(t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(t);e&&(o=o.filter(function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable})),r.push.apply(r,o)}return r}function a(t){for(var e=1;e<arguments.length;e++){var r=null!=arguments[e]?arguments[e]:{};e%2?l(Object(r),!0).forEach(function(e){var o,n,c;o=t,n=e,c=r[e],(n=function(t){var e=function(t,e){if("object"!=typeof t||!t)return t;var r=t[Symbol.toPrimitive];if(void 0!==r){var o=r.call(t,e||"default");if("object"!=typeof o)return o;throw TypeError("@@toPrimitive must return a primitive value.")}return("string"===e?String:Number)(t)}(t,"string");return"symbol"==typeof e?e:e+""}(n))in o?Object.defineProperty(o,n,{value:c,enumerable:!0,configurable:!0,writable:!0}):o[n]=c}):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(r)):l(Object(r)).forEach(function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(r,e))})}return t}function i(t){return r=>e.default.createElement(u,c({attr:a({},t.attr)},r),function t(r){return r&&r.map((r,o)=>e.default.createElement(r.tag,a({key:o},r.attr),t(r.child)))}(t.child))}function u(t){var l=r=>{var o,{attr:l,size:i,title:u}=t,s=function(t,e){if(null==t)return{};var r,o,n=function(t,e){if(null==t)return{};var r={};for(var o in t)if(Object.prototype.hasOwnProperty.call(t,o)){if(e.indexOf(o)>=0)continue;r[o]=t[o]}return r}(t,e);if(Object.getOwnPropertySymbols){var c=Object.getOwnPropertySymbols(t);for(o=0;o<c.length;o++)r=c[o],!(e.indexOf(r)>=0)&&Object.prototype.propertyIsEnumerable.call(t,r)&&(n[r]=t[r])}return n}(t,n),f=i||r.size||"1em";return r.className&&(o=r.className),t.className&&(o=(o?o+" ":"")+t.className),e.default.createElement("svg",c({stroke:"currentColor",fill:"currentColor",strokeWidth:"0"},r.attr,l,s,{className:o,style:a(a({color:t.color||r.color},r.style),t.style),height:f,width:f,xmlns:"http://www.w3.org/2000/svg"}),u&&e.default.createElement("title",null,u),t.children)};return void 0!==o?e.default.createElement(o.Consumer,null,t=>l(t)):l(r)}function s(t){return i({tag:"svg",attr:{viewBox:"0 0 288 512"},child:[{tag:"path",attr:{d:"M112 316.94v156.69l22.02 33.02c4.75 7.12 15.22 7.12 19.97 0L176 473.63V316.94c-10.39 1.92-21.06 3.06-32 3.06s-21.61-1.14-32-3.06zM144 0C64.47 0 0 64.47 0 144s64.47 144 144 144 144-64.47 144-144S223.53 0 144 0zm0 76c-37.5 0-68 30.5-68 68 0 6.62-5.38 12-12 12s-12-5.38-12-12c0-50.73 41.28-92 92-92 6.62 0 12 5.38 12 12s-5.38 12-12 12z"},child:[]}]})(t)}function f(t){return i({tag:"svg",attr:{viewBox:"0 0 512 512"},child:[{tag:"path",attr:{d:"M215.03 71.05L126.06 160H24c-13.26 0-24 10.74-24 24v144c0 13.25 10.74 24 24 24h102.06l88.97 88.95c15.03 15.03 40.97 4.47 40.97-16.97V88.02c0-21.46-25.96-31.98-40.97-16.97zM461.64 256l45.64-45.64c6.3-6.3 6.3-16.52 0-22.82l-22.82-22.82c-6.3-6.3-16.52-6.3-22.82 0L416 210.36l-45.64-45.64c-6.3-6.3-16.52-6.3-22.82 0l-22.82 22.82c-6.3 6.3-6.3 16.52 0 22.82L370.36 256l-45.63 45.63c-6.3 6.3-6.3 16.52 0 22.82l22.82 22.82c6.3 6.3 16.52 6.3 22.82 0L416 301.64l45.64 45.64c6.3 6.3 16.52 6.3 22.82 0l22.82-22.82c6.3-6.3 6.3-16.52 0-22.82L461.64 256z"},child:[]}]})(t)}function d(t){return i({tag:"svg",attr:{viewBox:"0 0 576 512"},child:[{tag:"path",attr:{d:"M215.03 71.05L126.06 160H24c-13.26 0-24 10.74-24 24v144c0 13.25 10.74 24 24 24h102.06l88.97 88.95c15.03 15.03 40.97 4.47 40.97-16.97V88.02c0-21.46-25.96-31.98-40.97-16.97zm233.32-51.08c-11.17-7.33-26.18-4.24-33.51 6.95-7.34 11.17-4.22 26.18 6.95 33.51 66.27 43.49 105.82 116.6 105.82 195.58 0 78.98-39.55 152.09-105.82 195.58-11.17 7.32-14.29 22.34-6.95 33.5 7.04 10.71 21.93 14.56 33.51 6.95C528.27 439.58 576 351.33 576 256S528.27 72.43 448.35 19.97zM480 256c0-63.53-32.06-121.94-85.77-156.24-11.19-7.14-26.03-3.82-33.12 7.46s-3.78 26.21 7.41 33.36C408.27 165.97 432 209.11 432 256s-23.73 90.03-63.48 115.42c-11.19 7.14-14.5 22.07-7.41 33.36 6.51 10.36 21.12 15.14 33.12 7.46C447.94 377.94 480 319.54 480 256zm-141.77-76.87c-11.58-6.33-26.19-2.16-32.61 9.45-6.39 11.61-2.16 26.2 9.45 32.61C327.98 228.28 336 241.63 336 256c0 14.38-8.02 27.72-20.92 34.81-11.61 6.41-15.84 21-9.45 32.61 6.43 11.66 21.05 15.8 32.61 9.45 28.23-15.55 45.77-45 45.77-76.88s-17.54-61.32-45.78-76.86z"},child:[]}]})(t)}t.s(["GenIcon",()=>i],40141),t.s(["FaMapPin",()=>s,"FaVolumeMute",()=>f,"FaVolumeUp",()=>d],11152)},6090,t=>{t.v({DialogButton:"DialogButton-module__LxvdOa__DialogButton",Secondary:"DialogButton-module__LxvdOa__Secondary DialogButton-module__LxvdOa__DialogButton"})}]);
|
||||
1
docs/_next/static/chunks/aed6ada67562a5fc.js
Normal file
1
docs/_next/static/chunks/aed6ada67562a5fc.js
Normal file
File diff suppressed because one or more lines are too long
1
docs/_next/static/chunks/b07469fc6c6cd3bb.js
Normal file
1
docs/_next/static/chunks/b07469fc6c6cd3bb.js
Normal file
File diff suppressed because one or more lines are too long
5
docs/_next/static/chunks/bd03a29a57c8ca45.css
Normal file
5
docs/_next/static/chunks/bd03a29a57c8ca45.css
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
.GuiMarkup-module__CLgNnq__GuiMarkup{white-space:pre-wrap;font-size:14px;line-height:1.5}.GuiMarkup-module__CLgNnq__GuiMarkup a{color:inherit;text-decoration:underline}.GuiMarkup-module__CLgNnq__Bullet{margin-left:.5em;margin-right:.5em}
|
||||
.DialogButton-module__LxvdOa__DialogButton{color:#99fff1;text-shadow:0 -1px 1px #0006;cursor:pointer;background:linear-gradient(#30a497cc,#1f9688cc 33%,#218377cc 67%,#046564cc);border:1px solid #387c74cc;border-top-color:#57b7b9cc;border-radius:4px;padding:4px 18px;font-family:inherit;font-size:14px;font-weight:500;box-shadow:inset 0 0 4px #02808e80}.DialogButton-module__LxvdOa__DialogButton:hover:not(:disabled){color:#b1fff5;border:1px solid #409188e6;border-top-color:#5ac6c2e6;box-shadow:inset 0 0 4px #02808e80,0 0 5px #3effbf80}.DialogButton-module__LxvdOa__DialogButton:active:not(:disabled){transform:translateY(1px)}.DialogButton-module__LxvdOa__DialogButton:disabled{opacity:.4;cursor:not-allowed}.DialogButton-module__LxvdOa__Secondary{box-shadow:none;color:#a2e2cfcc;text-shadow:none;background:0 0;border:1px solid #387c74cc;}.DialogButton-module__LxvdOa__Secondary:hover:not(:disabled){color:#a9ffe5cc;border:1px solid #3f9087e6}
|
||||
.ServerBrowser-module__kqm3eq__Dialog{color:#b0d5c9;-webkit-user-select:text;user-select:text;-webkit-touch-callout:default;background:#142526cc;border:1px solid #41838b99;border-radius:4px;outline:none;grid-template-rows:auto 1fr auto;grid-template-columns:100%;width:860px;max-width:calc(100dvw - 40px);height:560px;max-height:calc(100dvh - 40px);font-size:14px;line-height:1.5;display:grid;position:relative;overflow:hidden;box-shadow:0 0 50px #0006,inset 0 0 60px #01070d99}.ServerBrowser-module__kqm3eq__Overlay{z-index:10;background:#000000b3;justify-content:center;align-items:center;padding:20px;display:flex;position:fixed;inset:0}.ServerBrowser-module__kqm3eq__Header{border-bottom:1px solid #00bedc40;align-items:center;gap:12px;padding:12px 16px 10px;display:flex}.ServerBrowser-module__kqm3eq__Title{color:#7dffff;text-shadow:0 1px 6px #0006;flex:1;margin:0;font-size:18px;font-weight:500}.ServerBrowser-module__kqm3eq__RefreshButton{padding:3px 14px;font-size:12px;}.ServerBrowser-module__kqm3eq__ServerCount{color:#c9dcd866;font-size:12px}.ServerBrowser-module__kqm3eq__TableWrapper{min-height:0;overflow-y:auto}.ServerBrowser-module__kqm3eq__Table{border-collapse:collapse;width:100%;font-size:13px}.ServerBrowser-module__kqm3eq__Table th{text-align:left;cursor:pointer;-webkit-user-select:none;user-select:none;letter-spacing:.04em;text-transform:uppercase;color:#7dffff99;background:#0a191af2;border-bottom:1px solid #00bedc33;padding:6px 12px;font-size:11px;font-weight:500;position:sticky;top:0}.ServerBrowser-module__kqm3eq__Table th:hover{color:#7dffff}.ServerBrowser-module__kqm3eq__Table th:nth-child(2),.ServerBrowser-module__kqm3eq__Table td:nth-child(2),.ServerBrowser-module__kqm3eq__Table th:nth-child(3),.ServerBrowser-module__kqm3eq__Table td:nth-child(3){text-align:right}.ServerBrowser-module__kqm3eq__Table td{white-space:nowrap;text-overflow:ellipsis;border-bottom:1px solid #ffffff0a;max-width:340px;padding:3px 12px;font-weight:500;overflow:hidden}.ServerBrowser-module__kqm3eq__Table tbody tr{cursor:pointer}.ServerBrowser-module__kqm3eq__Table tbody tr:hover{background:#41838b1f}.ServerBrowser-module__kqm3eq__Selected{color:#1e2828;background:#5dffe1e6!important}.ServerBrowser-module__kqm3eq__PasswordIcon{color:#ffc83c99;margin-right:4px;font-size:11px}.ServerBrowser-module__kqm3eq__Empty{text-align:center;color:#c9dcd84d;font-style:italic;padding:32px 12px!important}.ServerBrowser-module__kqm3eq__Footer{background:#021415b3;border-top:1px solid #00bedc40;flex-shrink:0;align-items:center;gap:16px;padding:10px 12px;display:flex}.ServerBrowser-module__kqm3eq__JoinButton{}.ServerBrowser-module__kqm3eq__CloseButton{}.ServerBrowser-module__kqm3eq__Hint{color:#c9dcd84d;margin-left:auto;font-size:12px}@media (max-width:719px){.ServerBrowser-module__kqm3eq__Dialog{border-radius:0;width:100%;max-width:100dvw;height:100%;max-height:100dvh}.ServerBrowser-module__kqm3eq__Hint{display:none}.ServerBrowser-module__kqm3eq__Table td{max-width:200px}}
|
||||
.page-module__E0kJGG__CanvasContainer{z-index:0;position:absolute;inset:0}.page-module__E0kJGG__LoadingIndicator{pointer-events:none;z-index:1;opacity:.8;flex-direction:column;align-items:center;gap:16px;display:flex;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}.page-module__E0kJGG__LoadingIndicator[data-complete=true]{animation:.3s ease-out forwards page-module__E0kJGG__loadingComplete}.page-module__E0kJGG__Spinner{border:4px solid #fff3;border-top-color:#fff;border-radius:50%;width:48px;height:48px;animation:1s linear infinite page-module__E0kJGG__spin}.page-module__E0kJGG__Progress{background:#fff3;border-radius:2px;width:200px;height:4px;overflow:hidden}.page-module__E0kJGG__ProgressBar{background:#fff;border-radius:2px;height:100%;transition:width .1s ease-out}.page-module__E0kJGG__ProgressText{color:#ffffffb3;font-variant-numeric:tabular-nums;font-size:14px}@keyframes page-module__E0kJGG__spin{to{transform:rotate(360deg)}}@keyframes page-module__E0kJGG__loadingComplete{0%{opacity:1}to{opacity:0}}
|
||||
.MapInfoDialog-module__m0lXla__Dialog{color:#bccec3;-webkit-user-select:text;user-select:text;-webkit-touch-callout:default;background:#142526cc;border:1px solid #41838b99;border-radius:4px;outline:none;grid-template-rows:1fr auto;grid-template-columns:100%;width:800px;max-width:calc(100dvw - 40px);height:600px;max-height:calc(100dvh - 40px);font-size:14px;line-height:1.5;display:grid;position:relative;overflow:hidden;box-shadow:0 0 50px #0006,inset 0 0 60px #01070d99}.MapInfoDialog-module__m0lXla__Overlay{z-index:10;background:#000000b3;justify-content:center;align-items:center;padding:20px;display:flex;position:fixed;inset:0}.MapInfoDialog-module__m0lXla__Body{grid-template-rows:100%;grid-template-columns:1fr auto;min-height:0;display:grid;overflow:hidden}.MapInfoDialog-module__m0lXla__Left{padding:24px 28px;overflow-y:auto}.MapInfoDialog-module__m0lXla__PreviewImage{border-left:1px solid #00bedc40;height:100%;display:block}.MapInfoDialog-module__m0lXla__PreviewImageFloating{float:right;clear:right;width:auto;max-width:30%;max-height:260px;margin:0 0 16px 20px;display:block}.MapInfoDialog-module__m0lXla__Title{color:#7dffff;text-shadow:0 1px 6px #0006;margin:0;font-size:26px;font-weight:500}.MapInfoDialog-module__m0lXla__MapMeta{flex-wrap:wrap;gap:8px 16px;margin-bottom:4px;font-size:15px;font-weight:400;display:flex}.MapInfoDialog-module__m0lXla__MapPlanet{color:#dbcaa8b3}.MapInfoDialog-module__m0lXla__MapQuote{border-left:2px solid #00bedc59;margin:16px 0;padding:0 0 0 14px;font-style:italic}.MapInfoDialog-module__m0lXla__MapQuote p{white-space:pre-line;margin:0 0 4px}.MapInfoDialog-module__m0lXla__MapQuote cite{color:#ffffff73;font-size:12px;font-style:normal;display:block}.MapInfoDialog-module__m0lXla__MapBlurb{margin:0 0 16px;font-size:13px}.MapInfoDialog-module__m0lXla__Section{margin-top:20px}.MapInfoDialog-module__m0lXla__SectionTitle{color:#7dffff;letter-spacing:.04em;text-transform:uppercase;text-shadow:0 0 16px #00d2f040;margin:0 0 8px;font-size:16px;font-weight:500}.MapInfoDialog-module__m0lXla__MusicTrack{color:#cad0ac80;align-items:center;gap:6px;margin-top:16px;font-size:14px;font-style:italic;display:flex}.MapInfoDialog-module__m0lXla__MusicTrack[data-playing=true]{color:#f7fdd8b3}.MapInfoDialog-module__m0lXla__MusicButton{cursor:pointer;color:#557663;opacity:.5;background:0 0;border:0;border-radius:20px;flex-shrink:0;place-content:center;width:32px;height:32px;padding:0;font-size:20px;font-style:normal;line-height:1;display:grid}.MapInfoDialog-module__m0lXla__MusicTrack[data-playing=true] .MapInfoDialog-module__m0lXla__MusicButton{color:#6dffaa;opacity:1}.MapInfoDialog-module__m0lXla__MusicTrack[data-playing=true] .MapInfoDialog-module__m0lXla__MusicButton:hover{opacity:.7}.MapInfoDialog-module__m0lXla__Footer{background:#021415b3;border-top:1px solid #00bedc40;flex-shrink:0;align-items:center;gap:16px;padding:10px 12px;display:flex}.MapInfoDialog-module__m0lXla__CloseButton{}.MapInfoDialog-module__m0lXla__Hint{color:#c9dcd84d;margin-left:auto;font-size:12px}.MapInfoDialog-module__m0lXla__MusicTrackName{text-transform:capitalize}@media (max-width:719px){.MapInfoDialog-module__m0lXla__Body{display:block;overflow:auto}.MapInfoDialog-module__m0lXla__Hint{display:none}.MapInfoDialog-module__m0lXla__Left{width:100%;height:auto;margin:0;padding:16px 20px;overflow:auto}.MapInfoDialog-module__m0lXla__PreviewImage{width:auto;height:auto;margin:16px auto}.MapInfoDialog-module__m0lXla__CloseButton{width:220px;height:36px;margin:0 auto}}
|
||||
File diff suppressed because one or more lines are too long
1
docs/_next/static/chunks/ca289845e0f08110.js
Normal file
1
docs/_next/static/chunks/ca289845e0f08110.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,78462,e=>{e.v({PlayPause:"DemoPlaybackControls-module__A_AHSq__PlayPause",Root:"DemoPlaybackControls-module__A_AHSq__Root",Seek:"DemoPlaybackControls-module__A_AHSq__Seek",Speed:"DemoPlaybackControls-module__A_AHSq__Speed",Time:"DemoPlaybackControls-module__A_AHSq__Time"})},94737,e=>{"use strict";var t=e.i(43476),a=e.i(932),o=e.i(71645),n=e.i(32865),l=e.i(78462);let r=[.25,.5,1,2,4];function s(e){let t=Math.floor(e/60),a=Math.floor(e%60);return`${t}:${a.toString().padStart(2,"0")}`}function i(){let e,i,_,p,P,f,g,y,S,b,A,C,k=(0,a.c)(33),T=(0,n.useRecording)(),h=(0,n.useIsPlaying)(),v=(0,n.useCurrentTime)(),N=(0,n.useDuration)(),D=(0,n.useSpeed)(),{play:w,pause:x,seek:E,setSpeed:j}=(0,n.usePlaybackActions)();k[0]!==h||k[1]!==x||k[2]!==w||k[3]!==T?(e=()=>{if(!T)return;let e=e=>{if("Space"!==e.code)return;let t=e.target;"INPUT"===t.tagName||"TEXTAREA"===t.tagName||"SELECT"===t.tagName||"BUTTON"===t.tagName||t.isContentEditable||(e.preventDefault(),h?x():w())};return window.addEventListener("keydown",e),()=>window.removeEventListener("keydown",e)},i=[T,h,w,x],k[0]=h,k[1]=x,k[2]=w,k[3]=T,k[4]=e,k[5]=i):(e=k[4],i=k[5]),(0,o.useEffect)(e,i),k[6]!==E?(_=e=>{E(parseFloat(e.target.value))},k[6]=E,k[7]=_):_=k[7];let R=_;k[8]!==j?(p=e=>{j(parseFloat(e.target.value))},k[8]=j,k[9]=p):p=k[9];let q=p;if(!T||!Number.isFinite(T.duration))return null;let H=h?x:w,U=h?"Pause":"Play",$=h?"❚❚":"▶";k[10]!==H||k[11]!==U||k[12]!==$?(P=(0,t.jsx)("button",{className:l.default.PlayPause,onClick:H,"aria-label":U,children:$}),k[10]=H,k[11]=U,k[12]=$,k[13]=P):P=k[13],k[14]!==v?(f=s(v),k[14]=v,k[15]=f):f=k[15],k[16]!==N?(g=s(N),k[16]=N,k[17]=g):g=k[17];let B=`${f} / ${g}`;return k[18]!==B?(y=(0,t.jsx)("span",{className:l.default.Time,children:B}),k[18]=B,k[19]=y):y=k[19],k[20]!==v||k[21]!==N||k[22]!==R?(S=(0,t.jsx)("input",{className:l.default.Seek,type:"range",min:0,max:N,step:.01,value:v,onChange:R}),k[20]=v,k[21]=N,k[22]=R,k[23]=S):S=k[23],k[24]===Symbol.for("react.memo_cache_sentinel")?(b=r.map(u),k[24]=b):b=k[24],k[25]!==q||k[26]!==D?(A=(0,t.jsx)("select",{className:l.default.Speed,value:D,onChange:q,children:b}),k[25]=q,k[26]=D,k[27]=A):A=k[27],k[28]!==y||k[29]!==S||k[30]!==A||k[31]!==P?(C=(0,t.jsxs)("div",{className:l.default.Root,onKeyDown:m,onPointerDown:d,onClick:c,children:[P,y,S,A]}),k[28]=y,k[29]=S,k[30]=A,k[31]=P,k[32]=C):C=k[32],C}function u(e){return(0,t.jsxs)("option",{value:e,children:[e,"x"]},e)}function c(e){return e.stopPropagation()}function d(e){return e.stopPropagation()}function m(e){return e.stopPropagation()}e.s(["DemoPlaybackControls",()=>i])}]);
|
||||
File diff suppressed because one or more lines are too long
1
docs/_next/static/chunks/e16c267496b8de91.js
Normal file
1
docs/_next/static/chunks/e16c267496b8de91.js
Normal file
File diff suppressed because one or more lines are too long
17
docs/_next/static/chunks/e1a8caa90a5343cf.js
Normal file
17
docs/_next/static/chunks/e1a8caa90a5343cf.js
Normal file
File diff suppressed because one or more lines are too long
1
docs/_next/static/chunks/e5617268e3c7a140.js
Normal file
1
docs/_next/static/chunks/e5617268e3c7a140.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,64972,e=>{"use strict";var r=e.i(43476),t=e.i(932),i=e.i(71645),s=e.i(71753),o=e.i(47071),n=e.i(90072),a=e.i(85557),c=e.i(12979);let l=new n.Vector3,u=new n.Vector3,d=new n.Vector3,f=new n.Vector3,b=new n.Vector3,x=new n.Vector3,y=new n.Vector3(0,1,0);function h(e){let i,s,a,l,u,d=(0,t.c)(14),{visual:f}=e;d[0]!==f.texture?(i=(0,c.textureToUrl)(f.texture),d[0]=f.texture,d[1]=i):i=d[1];let b=i,x=(0,o.useTexture)(b,p),y=Array.isArray(x)?x[0]:x;d[2]!==f.color.b||d[3]!==f.color.g||d[4]!==f.color.r?(s=new n.Color().setRGB(f.color.r,f.color.g,f.color.b,n.SRGBColorSpace),d[2]=f.color.b,d[3]=f.color.g,d[4]=f.color.r,d[5]=s):s=d[5];let h=s;return d[6]!==f.size?(a=[f.size,f.size,1],d[6]=f.size,d[7]=a):a=d[7],d[8]!==h||d[9]!==y?(l=(0,r.jsx)("spriteMaterial",{map:y,color:h,transparent:!0,blending:n.AdditiveBlending,depthWrite:!1,toneMapped:!1}),d[8]=h,d[9]=y,d[10]=l):l=d[10],d[11]!==a||d[12]!==l?(u=(0,r.jsx)("sprite",{scale:a,children:l}),d[11]=a,d[12]=l,d[13]=u):u=d[13],u}function p(e){let r=Array.isArray(e)?e[0]:e;(0,a.setupEffectTexture)(r)}function m(e){let h,p,m,g,S,w,j,z,T,V,v,C,B=(0,t.c)(28),{entity:R,visual:U}=e,_=(0,i.useRef)(null),F=(0,i.useRef)(null),q=(0,i.useRef)(null);B[0]===Symbol.for("react.memo_cache_sentinel")?(h=new n.Quaternion,B[0]=h):h=B[0];let M=(0,i.useRef)(h);B[1]!==U.texture?(p=(0,c.textureToUrl)(U.texture),B[1]=U.texture,B[2]=p):p=B[2];let W=U.crossTexture??U.texture;B[3]!==W?(m=(0,c.textureToUrl)(W),B[3]=W,B[4]=m):m=B[4],B[5]!==p||B[6]!==m?(g=[p,m],B[5]=p,B[6]=m,B[7]=g):g=B[7];let G=g,P=(0,o.useTexture)(G,A);B[8]!==P?(S=Array.isArray(P)?P:[P,P],B[8]=P,B[9]=S):S=B[9];let[D,L]=S;return B[10]!==R||B[11]!==U.crossSize||B[12]!==U.crossViewAng||B[13]!==U.renderCross||B[14]!==U.tracerLength||B[15]!==U.tracerWidth?(w=e=>{let{camera:r}=e,t=_.current,i=F.current;if(!t||!i)return;let s=R.keyframes?.[0],o=s?.position,n=R.direction??s?.velocity;if(!o||!n||((0,a.torqueVecToThree)(n,l),1e-8>l.lengthSq())){t.visible=!1,q.current&&(q.current.visible=!1);return}l.normalize(),t.visible=!0,(0,a.torqueVecToThree)(o,x),u.copy(x).sub(r.position),d.crossVectors(u,l),1e-8>d.lengthSq()&&(d.crossVectors(y,l),1e-8>d.lengthSq()&&d.set(1,0,0)),d.normalize().multiplyScalar(U.tracerWidth);let c=.5*U.tracerLength;f.copy(l).multiplyScalar(-c),b.copy(l).multiplyScalar(c);let h=i.array;h[0]=f.x+d.x,h[1]=f.y+d.y,h[2]=f.z+d.z,h[3]=f.x-d.x,h[4]=f.y-d.y,h[5]=f.z-d.z,h[6]=b.x-d.x,h[7]=b.y-d.y,h[8]=b.z-d.z,h[9]=b.x+d.x,h[10]=b.y+d.y,h[11]=b.z+d.z,i.needsUpdate=!0;let p=q.current;if(!p)return;if(!U.renderCross){p.visible=!1;return}u.normalize();let m=l.dot(u);if(m>-U.crossViewAng&&m<U.crossViewAng){p.visible=!1;return}p.visible=!0,(0,a.setQuaternionFromDir)(l,M.current),p.quaternion.copy(M.current),p.scale.setScalar(U.crossSize)},B[10]=R,B[11]=U.crossSize,B[12]=U.crossViewAng,B[13]=U.renderCross,B[14]=U.tracerLength,B[15]=U.tracerWidth,B[16]=w):w=B[16],(0,s.useFrame)(w),B[17]===Symbol.for("react.memo_cache_sentinel")?(j=(0,r.jsx)("bufferAttribute",{ref:F,attach:"attributes-position",args:[new Float32Array(12),3]}),B[17]=j):j=B[17],B[18]===Symbol.for("react.memo_cache_sentinel")?(z=(0,r.jsx)("bufferAttribute",{attach:"attributes-uv",args:[new Float32Array([0,0,0,1,1,1,1,0]),2]}),B[18]=z):z=B[18],B[19]===Symbol.for("react.memo_cache_sentinel")?(T=(0,r.jsxs)("bufferGeometry",{children:[j,z,(0,r.jsx)("bufferAttribute",{attach:"index",args:[new Uint16Array([0,1,2,0,2,3]),1]})]}),B[19]=T):T=B[19],B[20]!==D?(V=(0,r.jsxs)("mesh",{ref:_,children:[T,(0,r.jsx)("meshBasicMaterial",{map:D,transparent:!0,blending:n.AdditiveBlending,side:n.DoubleSide,depthWrite:!1,toneMapped:!1})]}),B[20]=D,B[21]=V):V=B[21],B[22]!==L||B[23]!==U.renderCross?(v=U.renderCross?(0,r.jsxs)("mesh",{ref:q,children:[(0,r.jsxs)("bufferGeometry",{children:[(0,r.jsx)("bufferAttribute",{attach:"attributes-position",args:[new Float32Array([-.5,0,-.5,.5,0,-.5,.5,0,.5,-.5,0,.5]),3]}),(0,r.jsx)("bufferAttribute",{attach:"attributes-uv",args:[new Float32Array([0,0,0,1,1,1,1,0]),2]}),(0,r.jsx)("bufferAttribute",{attach:"index",args:[new Uint16Array([0,1,2,0,2,3]),1]})]}),(0,r.jsx)("meshBasicMaterial",{map:L,transparent:!0,blending:n.AdditiveBlending,side:n.DoubleSide,depthWrite:!1,toneMapped:!1})]}):null,B[22]=L,B[23]=U.renderCross,B[24]=v):v=B[24],B[25]!==V||B[26]!==v?(C=(0,r.jsxs)(r.Fragment,{children:[V,v]}),B[25]=V,B[26]=v,B[27]=C):C=B[27],C}function A(e){for(let r of Array.isArray(e)?e:[e])(0,a.setupEffectTexture)(r)}e.s(["SpriteProjectile",()=>h,"TracerProjectile",()=>m])}]);
|
||||
521
docs/_next/static/chunks/ee88398bb27ad4a1.js
Normal file
521
docs/_next/static/chunks/ee88398bb27ad4a1.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -8,7 +8,7 @@
|
|||
a:I[97367,["/t2-mapper/_next/static/chunks/2f236954d6a65e12.js"],"MetadataBoundary"]
|
||||
c:I[68027,["/t2-mapper/_next/static/chunks/2f236954d6a65e12.js"],"default"]
|
||||
:HL["/t2-mapper/_next/static/chunks/e620039d1c837dab.css","style"]
|
||||
0:{"P":null,"b":"6xhnTWazjCi9htjLDl3f1","c":["","_not-found",""],"q":"","i":false,"f":[[["",{"children":["/_not-found",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/e620039d1c837dab.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/89fcb9c19e93d0ef.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"defaultOptions":{"clearOnDefault":false},"children":["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]]}],{"children":[["$","$1","c",{"children":[null,["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}]],null,["$","$L5",null,{"children":["$","$6",null,{"name":"Next.MetadataOutlet","children":"$@7"}]}]]}],{},null,false,false]},null,false,false]},null,false,false],["$","$1","h",{"children":[["$","meta",null,{"name":"robots","content":"noindex"}],["$","$L8",null,{"children":"$L9"}],["$","div",null,{"hidden":true,"children":["$","$La",null,{"children":["$","$6",null,{"name":"Next.Metadata","children":"$Lb"}]}]}],null]}],false]],"m":"$undefined","G":["$c","$undefined"],"S":true}
|
||||
0:{"P":null,"b":"JablvlklHXp4NGWk4TTlC","c":["","_not-found",""],"q":"","i":false,"f":[[["",{"children":["/_not-found",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/e620039d1c837dab.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/89fcb9c19e93d0ef.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"defaultOptions":{"clearOnDefault":false},"children":["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]]}],{"children":[["$","$1","c",{"children":[null,["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}]],null,["$","$L5",null,{"children":["$","$6",null,{"name":"Next.MetadataOutlet","children":"$@7"}]}]]}],{},null,false,false]},null,false,false]},null,false,false],["$","$1","h",{"children":[["$","meta",null,{"name":"robots","content":"noindex"}],["$","$L8",null,{"children":"$L9"}],["$","div",null,{"hidden":true,"children":["$","$La",null,{"children":["$","$6",null,{"name":"Next.Metadata","children":"$Lb"}]}]}],null]}],false]],"m":"$undefined","G":["$c","$undefined"],"S":true}
|
||||
9:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"}]]
|
||||
d:I[27201,["/t2-mapper/_next/static/chunks/2f236954d6a65e12.js"],"IconMark"]
|
||||
7:null
|
||||
|
|
|
|||
|
|
@ -3,4 +3,4 @@
|
|||
3:I[97367,["/t2-mapper/_next/static/chunks/2f236954d6a65e12.js"],"MetadataBoundary"]
|
||||
4:"$Sreact.suspense"
|
||||
5:I[27201,["/t2-mapper/_next/static/chunks/2f236954d6a65e12.js"],"IconMark"]
|
||||
0:{"buildId":"6xhnTWazjCi9htjLDl3f1","rsc":["$","$1","h",{"children":[["$","meta",null,{"name":"robots","content":"noindex"}],["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"MapGenius – Explore maps for Tribes 2"}],["$","meta","1",{"name":"description","content":"Tribes 2 forever."}],["$","link","2",{"rel":"icon","href":"/t2-mapper/icon.png?icon.2911bba1.png","sizes":"108x128","type":"image/png"}],["$","$L5","3",{}]]}]}]}],null]}],"loading":null,"isPartial":false}
|
||||
0:{"buildId":"JablvlklHXp4NGWk4TTlC","rsc":["$","$1","h",{"children":[["$","meta",null,{"name":"robots","content":"noindex"}],["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"MapGenius – Explore maps for Tribes 2"}],["$","meta","1",{"name":"description","content":"Tribes 2 forever."}],["$","link","2",{"rel":"icon","href":"/t2-mapper/icon.png?icon.2911bba1.png","sizes":"108x128","type":"image/png"}],["$","$L5","3",{}]]}]}]}],null]}],"loading":null,"isPartial":false}
|
||||
|
|
|
|||
|
|
@ -3,4 +3,4 @@
|
|||
3:I[39756,["/t2-mapper/_next/static/chunks/2f236954d6a65e12.js"],"default"]
|
||||
4:I[37457,["/t2-mapper/_next/static/chunks/2f236954d6a65e12.js"],"default"]
|
||||
:HL["/t2-mapper/_next/static/chunks/e620039d1c837dab.css","style"]
|
||||
0:{"buildId":"6xhnTWazjCi9htjLDl3f1","rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/e620039d1c837dab.css","precedence":"next"}],["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/89fcb9c19e93d0ef.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"defaultOptions":{"clearOnDefault":false},"children":["$","$L3",null,{"parallelRouterKey":"children","template":["$","$L4",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]]}],"loading":null,"isPartial":false}
|
||||
0:{"buildId":"JablvlklHXp4NGWk4TTlC","rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/e620039d1c837dab.css","precedence":"next"}],["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/89fcb9c19e93d0ef.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"defaultOptions":{"clearOnDefault":false},"children":["$","$L3",null,{"parallelRouterKey":"children","template":["$","$L4",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]}]]}],"loading":null,"isPartial":false}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
1:"$Sreact.fragment"
|
||||
2:I[97367,["/t2-mapper/_next/static/chunks/2f236954d6a65e12.js"],"OutletBoundary"]
|
||||
3:"$Sreact.suspense"
|
||||
0:{"buildId":"6xhnTWazjCi9htjLDl3f1","rsc":["$","$1","c",{"children":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],null,["$","$L2",null,{"children":["$","$3",null,{"name":"Next.MetadataOutlet","children":"$@4"}]}]]}],"loading":null,"isPartial":false}
|
||||
0:{"buildId":"JablvlklHXp4NGWk4TTlC","rsc":["$","$1","c",{"children":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],null,["$","$L2",null,{"children":["$","$3",null,{"name":"Next.MetadataOutlet","children":"$@4"}]}]]}],"loading":null,"isPartial":false}
|
||||
4:null
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
1:"$Sreact.fragment"
|
||||
2:I[39756,["/t2-mapper/_next/static/chunks/2f236954d6a65e12.js"],"default"]
|
||||
3:I[37457,["/t2-mapper/_next/static/chunks/2f236954d6a65e12.js"],"default"]
|
||||
0:{"buildId":"6xhnTWazjCi9htjLDl3f1","rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"loading":null,"isPartial":false}
|
||||
0:{"buildId":"JablvlklHXp4NGWk4TTlC","rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"loading":null,"isPartial":false}
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
:HL["/t2-mapper/_next/static/chunks/e620039d1c837dab.css","style"]
|
||||
0:{"buildId":"6xhnTWazjCi9htjLDl3f1","tree":{"name":"","paramType":null,"paramKey":"","hasRuntimePrefetch":false,"slots":{"children":{"name":"/_not-found","paramType":null,"paramKey":"/_not-found","hasRuntimePrefetch":false,"slots":{"children":{"name":"__PAGE__","paramType":null,"paramKey":"__PAGE__","hasRuntimePrefetch":false,"slots":null,"isRootLayout":false}},"isRootLayout":false}},"isRootLayout":true},"staleTime":300}
|
||||
0:{"buildId":"JablvlklHXp4NGWk4TTlC","tree":{"name":"","paramType":null,"paramKey":"","hasRuntimePrefetch":false,"slots":{"children":{"name":"/_not-found","paramType":null,"paramKey":"/_not-found","hasRuntimePrefetch":false,"slots":{"children":{"name":"__PAGE__","paramType":null,"paramKey":"__PAGE__","hasRuntimePrefetch":false,"slots":null,"isRootLayout":false}},"isRootLayout":false}},"isRootLayout":true},"staleTime":300}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -8,7 +8,7 @@
|
|||
a:I[97367,["/t2-mapper/_next/static/chunks/2f236954d6a65e12.js"],"MetadataBoundary"]
|
||||
c:I[68027,["/t2-mapper/_next/static/chunks/2f236954d6a65e12.js"],"default"]
|
||||
:HL["/t2-mapper/_next/static/chunks/e620039d1c837dab.css","style"]
|
||||
0:{"P":null,"b":"6xhnTWazjCi9htjLDl3f1","c":["","_not-found",""],"q":"","i":false,"f":[[["",{"children":["/_not-found",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/e620039d1c837dab.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/89fcb9c19e93d0ef.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"defaultOptions":{"clearOnDefault":false},"children":["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]]}],{"children":[["$","$1","c",{"children":[null,["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}]],null,["$","$L5",null,{"children":["$","$6",null,{"name":"Next.MetadataOutlet","children":"$@7"}]}]]}],{},null,false,false]},null,false,false]},null,false,false],["$","$1","h",{"children":[["$","meta",null,{"name":"robots","content":"noindex"}],["$","$L8",null,{"children":"$L9"}],["$","div",null,{"hidden":true,"children":["$","$La",null,{"children":["$","$6",null,{"name":"Next.Metadata","children":"$Lb"}]}]}],null]}],false]],"m":"$undefined","G":["$c","$undefined"],"S":true}
|
||||
0:{"P":null,"b":"JablvlklHXp4NGWk4TTlC","c":["","_not-found",""],"q":"","i":false,"f":[[["",{"children":["/_not-found",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/e620039d1c837dab.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/89fcb9c19e93d0ef.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"defaultOptions":{"clearOnDefault":false},"children":["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]]}],{"children":[["$","$1","c",{"children":[null,["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}]],null,["$","$L5",null,{"children":["$","$6",null,{"name":"Next.MetadataOutlet","children":"$@7"}]}]]}],{},null,false,false]},null,false,false]},null,false,false],["$","$1","h",{"children":[["$","meta",null,{"name":"robots","content":"noindex"}],["$","$L8",null,{"children":"$L9"}],["$","div",null,{"hidden":true,"children":["$","$La",null,{"children":["$","$6",null,{"name":"Next.Metadata","children":"$Lb"}]}]}],null]}],false]],"m":"$undefined","G":["$c","$undefined"],"S":true}
|
||||
9:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"}]]
|
||||
d:I[27201,["/t2-mapper/_next/static/chunks/2f236954d6a65e12.js"],"IconMark"]
|
||||
7:null
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1,70 +1,87 @@
|
|||
// Tribes 2 Unofficial Authentication System
|
||||
// http://www.tribesnext.com/
|
||||
// Written by Electricutioner/Thyth
|
||||
// Copyright 2008 by Electricutioner/Thyth and the Tribes 2 Community System Reengineering Intitiative
|
||||
|
||||
// Authentication Server Connector Version 1.0: 11/06/2008
|
||||
|
||||
function authConnect_findAuthServer()
|
||||
{
|
||||
if ($AuthServer::Address !$= "")
|
||||
return;
|
||||
|
||||
echo("Looking up Authentication Server...");
|
||||
|
||||
if (isObject(AuthConnection))
|
||||
{
|
||||
AuthConnection.disconnect();
|
||||
AuthConnection.delete();
|
||||
}
|
||||
|
||||
new HTTPObject(AuthConnection);
|
||||
AuthConnection.setHeader("Accept", "text/plain");
|
||||
AuthConnection.get("www.tribesnext.com", "auth");
|
||||
}
|
||||
|
||||
function AuthConnection::onLine(%this, %line)
|
||||
{
|
||||
if (getFieldCount(%line) != 2)
|
||||
return;
|
||||
|
||||
%address = getField(%line, 0);
|
||||
%signature = getField(%line, 1);
|
||||
|
||||
%sha1sum = sha1sum(%address);
|
||||
%verifSum = t2csri_verify_auth_signature(%signature);
|
||||
while (strlen(%verifSum) < 40)
|
||||
%verifSum = "0" @ %verifSum;
|
||||
|
||||
if (%sha1sum !$= %verifSum)
|
||||
{
|
||||
error("Authentication server lookup returned an address with an invalid signature.");
|
||||
error("Unable to contact the authentication server.");
|
||||
$AuthServer::Address = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
echo("Authentication server found at " @ %address @ ". Ready to authenticate.");
|
||||
$AuthServer::Address = %address;
|
||||
}
|
||||
}
|
||||
|
||||
function AuthConnection::onDisconnect(%this)
|
||||
{
|
||||
if ($AuthServer::Address $= "")
|
||||
{
|
||||
error("Authentication server lookup failed.");
|
||||
}
|
||||
|
||||
%this.delete();
|
||||
}
|
||||
|
||||
function AuthConnection::onConnectFailed(%this)
|
||||
{
|
||||
%this.delete();
|
||||
}
|
||||
|
||||
function AuthConnection::onDNSFailed(%this)
|
||||
{
|
||||
%this.delete();
|
||||
}
|
||||
// Tribes 2 Unofficial Authentication System
|
||||
// http://www.tribesnext.com/
|
||||
// Written by Electricutioner/Thyth
|
||||
// Copyright 2008 by Electricutioner/Thyth and the Tribes 2 Community System Reengineering Intitiative
|
||||
|
||||
// Authentication Server Connector Version 1.0: 11/06/2008
|
||||
|
||||
function authConnect_findAuthServer()
|
||||
{
|
||||
if ($AuthServer::Address !$= "")
|
||||
return;
|
||||
echo("Looking up Authentication Server...");
|
||||
if (isObject(AuthConnection))
|
||||
{
|
||||
AuthConnection.disconnect();
|
||||
AuthConnection.delete();
|
||||
}
|
||||
new TCPObject(AuthConnection);
|
||||
|
||||
%data = "GET /auth HTTP/1.1\r\nHost: www.tribesnext.com\r\nUser-Agent: Tribes 2\r\nConnection: close\r\n\r\n";
|
||||
AuthConnection.data = %data;
|
||||
AuthConnection.connect("www.tribesnext.com:80");
|
||||
$AuthServer::Primed = 0;
|
||||
}
|
||||
|
||||
function AuthConnection::onLine(%this, %line)
|
||||
{
|
||||
if (%line == 411)
|
||||
return;
|
||||
if (trim(%line) $= "")
|
||||
{
|
||||
$AuthServer::Primed = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
if ($AuthServer::Primed)
|
||||
{
|
||||
$AuthServer::Address = %line;
|
||||
%this.disconnect();
|
||||
authConnect_verifyLookup();
|
||||
}
|
||||
}
|
||||
|
||||
function AuthConnection::onConnected(%this)
|
||||
{
|
||||
%this.send(%this.data);
|
||||
}
|
||||
|
||||
function authConnect_verifyLookup()
|
||||
{
|
||||
|
||||
if (getFieldCount($AuthServer::Address) != 2)
|
||||
{
|
||||
$AuthServer::Address = "";
|
||||
error("Authentication server lookup failed.");
|
||||
return;
|
||||
}
|
||||
%address = getField($AuthServer::Address, 0);
|
||||
%signature = getField($AuthServer::Address, 1);
|
||||
|
||||
%sha1sum = sha1sum(%address);
|
||||
%verifSum = t2csri_verify_auth_signature(%signature);
|
||||
while (strlen(%verifSum) < 40)
|
||||
%verifSum = "0" @ %verifSum;
|
||||
if (%sha1sum !$= %verifSum)
|
||||
{
|
||||
// signature verification failed... someone has subverted the auth server lookup
|
||||
error("Authentication server lookup returned an address with an invalid signature.");
|
||||
error("Unable to contact the authentication server.");
|
||||
$AuthServer::Address = "";
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
echo("Authentication server found at " @ %address @ ". Ready to authenticate.");
|
||||
$AuthServer::Address = %address;
|
||||
$AuthServer::Primed = "";
|
||||
}
|
||||
}
|
||||
|
||||
// perform signature verification to prove that the auth server has designated the
|
||||
// provided address
|
||||
function t2csri_verify_auth_signature(%sig)
|
||||
{
|
||||
rubyEval("tsEval '$temp=\"' + t2csri_verify_auth_signature('" @ %sig @ "').to_s(16) + '\";'");
|
||||
return $temp;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,9 +14,9 @@ $Authentication::Settings::Timeout = 30000;
|
|||
|
||||
function AuthenticationInterface::onLine(%this, %line)
|
||||
{
|
||||
//warn(%line);
|
||||
if (isEventPending($Authentication::TransactionCompletionSchedule))
|
||||
cancel($Authentication::TransactionCompletionSchedule);
|
||||
|
||||
$Authentication::TransactionCompletionSchedule = schedule(700, 0, Authentication_transactionComplete);
|
||||
|
||||
if ($Authentication::Status::ActiveMode != 0)
|
||||
|
|
@ -102,8 +102,12 @@ function Authentication_transactionComplete()
|
|||
}
|
||||
else if (getWord(%buffer, 0) $= "CERT:")
|
||||
{
|
||||
$Authentication::Status::LastCert = getRecord(%buffer, 0);
|
||||
$Authentication::Status::LastExp = getRecord(%buffer, 1);
|
||||
%cert = getSubStr(%buffer, 0, strstr(%buffer, "\n"));
|
||||
%buffer = getSubStr(%buffer, strstr(%buffer, "\n") + 1, strlen(%buffer));
|
||||
%exp = getSubStr(%buffer, 0, (strstr(%buffer, "\n") == -1 ? strlen(%buffer) : strstr(%buffer, "\n")));
|
||||
|
||||
$Authentication::Status::LastCert = %cert;
|
||||
$Authentication::Status::LastExp = %exp;
|
||||
echo("Authentication: Successfully downloaded certificate and encrypted key.");
|
||||
}
|
||||
else
|
||||
|
|
@ -163,7 +167,6 @@ function Authentication_checkAvail()
|
|||
|
||||
if (isObject(AuthenticationInterface))
|
||||
AuthenticationInterface.delete();
|
||||
|
||||
new TCPObject(AuthenticationInterface);
|
||||
|
||||
AuthenticationInterface.data = "AVAIL\n";
|
||||
|
|
@ -185,7 +188,6 @@ function Authentication_checkName(%name)
|
|||
|
||||
if (isObject(AuthenticationInterface))
|
||||
AuthenticationInterface.delete();
|
||||
|
||||
new TCPObject(AuthenticationInterface);
|
||||
|
||||
AuthenticationInterface.data = "NAME\t" @ %name @ "\n";
|
||||
|
|
@ -207,7 +209,6 @@ function Authentication_recoverAccount(%payload)
|
|||
|
||||
if (isObject(AuthenticationInterface))
|
||||
AuthenticationInterface.delete();
|
||||
|
||||
new TCPObject(AuthenticationInterface);
|
||||
|
||||
AuthenticationInterface.data = "RECOVER\t" @ %payload @ "\n";
|
||||
|
|
@ -229,11 +230,9 @@ function Authentication_registerAccount(%payload)
|
|||
|
||||
if (isObject(AuthenticationInterface))
|
||||
AuthenticationInterface.delete();
|
||||
|
||||
new TCPObject(AuthenticationInterface);
|
||||
|
||||
AuthenticationInterface.data = "SIGN\t" @ %payload @ "\n";
|
||||
|
||||
AuthenticationInterface.connect($AuthServer::Address);
|
||||
$Authentication::TransactionCompletionSchedule = schedule($Authentication::Settings::Timeout, 0, Authentication_transactionComplete);
|
||||
}
|
||||
111
docs/base/@vl2/T2csri.vl2/t2csri/autoupdate.cs
Normal file
111
docs/base/@vl2/T2csri.vl2/t2csri/autoupdate.cs
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
// Tribes 2 Unofficial Authentication System
|
||||
// http://www.tribesnext.com/
|
||||
// Written by Electricutioner/Thyth
|
||||
// Copyright 2008 by Electricutioner/Thyth and the Tribes 2 Community System Reengineering Intitiative
|
||||
|
||||
// Bare Bones Auto Update System Version 1.0: 11/06/2008
|
||||
|
||||
function authConnect_findAutoUpdater()
|
||||
{
|
||||
if ($AutoUpdater::Address !$= "")
|
||||
return;
|
||||
|
||||
if (isObject(AutoUpdateConnection))
|
||||
{
|
||||
AutoUpdateConnection.disconnect();
|
||||
AutoUpdateConnection.delete();
|
||||
}
|
||||
new TCPObject(AutoUpdateConnection);
|
||||
|
||||
%data = "GET /update HTTP/1.1\r\nHost: www.tribesnext.com\r\nUser-Agent: Tribes 2\r\nConnection: close\r\n\r\n";
|
||||
AutoUpdateConnection.connect("www.tribesnext.com:80");
|
||||
AutoUpdateConnection.schedule(1000, send, %data);
|
||||
}
|
||||
|
||||
function AutoUpdateConnection::onLine(%this, %line)
|
||||
{
|
||||
if (!$AutoUpdater::UpdateFound)
|
||||
{
|
||||
$AutoUpdater::Address = %line;
|
||||
%this.disconnect();
|
||||
autoUpdate_verifyLookup();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isEventPending($AutoUpdate::LastLineSch))
|
||||
cancel($AutoUpdate::LastLineSch);
|
||||
$AutoUpdate::LastLineSch = autoUpdate_applyUpdate();
|
||||
if ($AutoUpdate::UpdateStarted)
|
||||
$AutoUpdate::Buffer = $AutoUpdate::Buffer @ "\n" @ %line;
|
||||
else if (strlen(%line) == 0)
|
||||
$AutoUpdate::UpdateStarted = 1;
|
||||
}
|
||||
}
|
||||
|
||||
function autoUpdate_verifyLookup()
|
||||
{
|
||||
if (getFieldCount($AutoUpdate::Address) != 2)
|
||||
{
|
||||
$AutoUpdater::Address = "";
|
||||
error("No valid update address found.");
|
||||
return;
|
||||
}
|
||||
%address = getField($AutoUpdater::Address, 0);
|
||||
%signature = getField($AutoUpdater::Address, 1);
|
||||
|
||||
%sha1sum = sha1sum(%address);
|
||||
if (%sha1sum !$= t2csri_verify_update_signature(%signature))
|
||||
{
|
||||
// signature verification failed... someone has subverted the auth server lookup
|
||||
error("Auto update lookup returned an address with an invalid signature.");
|
||||
error("Unable to download update without a correct signature.");
|
||||
$AutoUpdater::Address = "";
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
echo("New update found at " @ %address @ ". Ready to download.");
|
||||
$AutoUpdater::Address = %address;
|
||||
$AutoUpdater::UpdateFound = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// perform signature verification to prove that the update server has designated the
|
||||
// provided URL for a download, we don't want people injecting arbitrary code into
|
||||
// user installations
|
||||
function t2csri_verify_update_signature(%sig)
|
||||
{
|
||||
rubyEval("tsEval '$temp=\"' + t2csri_verify_update_signature('" @ %sig @ "') + '\";'");
|
||||
return $temp;
|
||||
}
|
||||
|
||||
function autoUpdate_performUpdate()
|
||||
{
|
||||
if ($AutoUpdater::Address $= "")
|
||||
return;
|
||||
|
||||
if (isObject(AutoUpdateConnection))
|
||||
{
|
||||
AutoUpdateConnection.disconnect();
|
||||
AutoUpdateConnection.delete();
|
||||
}
|
||||
new TCPObject(AutoUpdateConnection);
|
||||
|
||||
%host = getSubStr($AutoUpdater::Address, 0, strstr("/"));
|
||||
%uri = getSubStr($AutoUpdater::Address, strlen(%host), strlen($AutoUpdater::Address));
|
||||
|
||||
%data = "GET " @ %uri @ " HTTP/1.1\nHost: " @ %host @ "\nUser-Agent: Tribes 2\nConnection: close\n\n";
|
||||
AutoUpdateConnection.connect(%host);
|
||||
AutoUpdateConnection.schedule(1000, send, %data);
|
||||
}
|
||||
|
||||
function autoUpdate_applyUpdate()
|
||||
{
|
||||
new FileObject(AutoUpdateFile);
|
||||
AutoUpdateFile.openForWrite("autoUpdate.rb");
|
||||
AutoUpdateFile.writeline($AutoUpdate::Buffer);
|
||||
AutoUpdateFile.close();
|
||||
AutoUpdateFile.delete();
|
||||
|
||||
rubyExec("autoUpdate.rb");
|
||||
}
|
||||
93
docs/base/@vl2/T2csri.vl2/t2csri/bans.cs
Normal file
93
docs/base/@vl2/T2csri.vl2/t2csri/bans.cs
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
// Tribes 2 Unofficial Authentication System
|
||||
// http://www.tribesnext.com/
|
||||
// Written by Electricutioner/Thyth
|
||||
// Copyright 2008 by Electricutioner/Thyth and the Tribes 2 Community System Reengineering Intitiative
|
||||
|
||||
// IP and GUID ban list handling.
|
||||
// These seem to be completely broken in engine, so... here is a script implementation.
|
||||
|
||||
// Still works the same way as before... so scripts will function unmodified.
|
||||
// BanList::add( %guid, %ipAddress, %seconds);
|
||||
// If both GUID and IP address are specified, both types of entries are made on the banlist.
|
||||
|
||||
// gets the current Unix Epoch time from Ruby -- in seconds
|
||||
function currentEpochTime()
|
||||
{
|
||||
rubyEval("tsEval '$temp=' + Time.now.to_i.to_s + ';'");
|
||||
return $temp;
|
||||
}
|
||||
|
||||
// compute the addition in Ruby, due to the Torque script precision problems for >1e6 values
|
||||
function getEpochOffset(%seconds)
|
||||
{
|
||||
rubyEval("tsEval '$temp=' + (Time.now.to_i + " @ %seconds @ ").to_s + ';'");
|
||||
return $temp;
|
||||
}
|
||||
|
||||
// bans are added to the $BanList::GUID and $BanList::IP hash maps as the Unix epoch time
|
||||
// when the ban will expire
|
||||
function BanList::add(%guid, %ipAddress, %seconds)
|
||||
{
|
||||
if (%guid != 0)
|
||||
{
|
||||
// add GUID ban
|
||||
$BanList::GUID[%guid] = getEpochOffset(%seconds);
|
||||
}
|
||||
if (getSubStr(%ipAddress, 0, 3) $= "IP:")
|
||||
{
|
||||
// add IP ban
|
||||
%bareIP = getSubStr(%ipAddress, 3, strLen(%ipAddress));
|
||||
%bareIP = getSubStr(%bareIP, 0, strstr(%bareIP, ":"));
|
||||
%bareIP = strReplace(%bareIP, ".", "_"); // variable access bug workaround
|
||||
|
||||
$BanList::IP[%bareIP] = getEpochOffset(%seconds);
|
||||
}
|
||||
|
||||
// write out the updated bans to the file
|
||||
export("$BanList*", "prefs/banlist.cs");
|
||||
}
|
||||
|
||||
// returns boolean on whether the given client is IP banned or not
|
||||
// true if banned, false if not banned
|
||||
function banList_checkIP(%client)
|
||||
{
|
||||
%ip = %client.getAddress();
|
||||
%ip = getSubStr(%ip, 3, strLen(%ip));
|
||||
%ip = getSubStr(%ip, 0, strstr(%ip, ":"));
|
||||
%ip = strReplace(%ip, ".", "_");
|
||||
|
||||
%time = $BanList::IP[%ip];
|
||||
if (%time !$= "")
|
||||
{
|
||||
//%delta = %time - currentEpochTime();
|
||||
// T2 arithmetic fail again... doing subtraction in Ruby
|
||||
rubyEval("tsEval '$temp=' + (" @ %time @ " - Time.now.to_i).to_s + ';'");
|
||||
%delta = $temp;
|
||||
|
||||
if (%delta > 0)
|
||||
return 1;
|
||||
else
|
||||
deleteVariables("$BanList::IP" @ %ip);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// returns boolean on whether the given GUID is banned or not
|
||||
// true if banned, false if not banned
|
||||
function banList_checkGUID(%guid)
|
||||
{
|
||||
%time = $BanList::GUID[%guid];
|
||||
if (%time !$= "")
|
||||
{
|
||||
//%delta = %time - currentEpochTime();
|
||||
// T2 arithmetic fail again... doing subtraction in Ruby
|
||||
rubyEval("tsEval '$temp=' + (" @ %time @ " - Time.now.to_i).to_s + ';'");
|
||||
%delta = $temp;
|
||||
|
||||
if (%delta > 0)
|
||||
return 1;
|
||||
else
|
||||
deleteVariables("$BanList::GUID" @ %guid);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
164
docs/base/@vl2/T2csri.vl2/t2csri/base64.cs
Normal file
164
docs/base/@vl2/T2csri.vl2/t2csri/base64.cs
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
// Torque Script Base64 Utilities
|
||||
// Written by Electricutioner
|
||||
// 10:43 PM 7/13/2005
|
||||
|
||||
// Used under license by the Tribes 2 Community System Re-engineering Intitiative.
|
||||
// License Granted: 10/31/2008
|
||||
|
||||
// necessary for the transfer of arbitrary binary data over ASCII connections
|
||||
function Base64_Encode(%string)
|
||||
{
|
||||
%encoded = "";
|
||||
for (%i = 0; %i < strLen(%string); %i += 3)
|
||||
{
|
||||
%binBlock = "";
|
||||
for (%j = 0; %j < 3; %j++)
|
||||
{
|
||||
%bin = DecToBin(strCmp(getSubStr(%string, %i + %j, 1), ""));
|
||||
while (strLen(%bin) < 8 && strLen(%bin) != 0)
|
||||
%bin = "0" @ %bin;
|
||||
%binBlock = %binBlock @ %bin;
|
||||
}
|
||||
for (%j = 0; %j < 4; %j++)
|
||||
{
|
||||
%bin = getSubStr(%binBlock, 6 * %j, 6);
|
||||
if (%bin !$= "")
|
||||
{
|
||||
while(strLen(%bin) < 6)
|
||||
%bin = %bin @ "0";
|
||||
%encoded = %encoded @ $Base64Utils::Base64Chars[BinToDec(%bin)];
|
||||
}
|
||||
else
|
||||
%encoded = %encoded @ "=";
|
||||
}
|
||||
}
|
||||
return %encoded;
|
||||
}
|
||||
function Base64_Decode(%string)
|
||||
{
|
||||
%decoded = "";
|
||||
for (%i = 0; %i < strLen(%string); %i += 4)
|
||||
{
|
||||
%binBlock = "";
|
||||
for (%j = 0; %j < 4; %j++)
|
||||
{
|
||||
%bin = "";
|
||||
%val = Base64_ValToIndex(strCmp(getSubStr(%string, %i + %j, 1), ""));
|
||||
if (%val != -1)
|
||||
%bin = DecToBin(%val);
|
||||
while (strLen(%bin) < 6 && %val != -1)
|
||||
%bin = "0" @ %bin;
|
||||
%binBlock = %binBlock @ %bin;
|
||||
}
|
||||
for (%j = 0; %j < 3; %j++)
|
||||
{
|
||||
%bin = getSubStr(%binBlock, 8 * %j, 8);
|
||||
while(strLen(%bin) < 8 && strLen(%bin) != 0)
|
||||
%bin = "0" @ %bin;
|
||||
if (%bin !$= "")
|
||||
%decoded = %decoded @ collapseEscape("\\x" @ DecToHex(BinToDec(%bin)));
|
||||
}
|
||||
}
|
||||
|
||||
return %decoded;
|
||||
}
|
||||
// a few conditionals are better than a loop
|
||||
function Base64_ValToIndex(%val)
|
||||
{
|
||||
if (%val > 96 && %val < 123)
|
||||
return %val - 71;
|
||||
else if (%val > 64 && %val < 91)
|
||||
return %val - 65;
|
||||
else if (%val > 47 && %val < 58)
|
||||
return %val + 4;
|
||||
else if (%val == 43)
|
||||
return 62;
|
||||
else if (%val == 47)
|
||||
return 63;
|
||||
else if (%val == 61)
|
||||
return -1;
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
//create the character array in a minimum of fuss
|
||||
function Base64_CreateArray()
|
||||
{
|
||||
for (%i = 0; %i < 26; %i++)
|
||||
{
|
||||
$Base64Utils::Base64Chars[%i] = collapseEscape("\\x" @ DecToHex(65 + %i));
|
||||
$Base64Utils::Base64Chars[%i + 26] = collapseEscape("\\x" @ DecToHex(97 + %i));
|
||||
|
||||
if (%i < 10)
|
||||
$Base64Utils::Base64Chars[%i + 52] = %i;
|
||||
}
|
||||
$Base64Utils::Base64Chars[62] = "+";
|
||||
$Base64Utils::Base64Chars[63] = "/";
|
||||
}
|
||||
|
||||
// these binary conversion functions are much better than older ones
|
||||
// these can handle just about any size of input, unlike 8 bit like the previous ones
|
||||
function DecToBin(%dec)
|
||||
{
|
||||
%length = mCeil(mLog(%dec) / mLog(2));
|
||||
%bin = "";
|
||||
for (%i = 0; %i <= %length; %i++)
|
||||
{
|
||||
%test = mPow(2, %length - %i);
|
||||
if (%dec >= %test)
|
||||
{
|
||||
%bin = %bin @ "1";
|
||||
%dec -= %test;
|
||||
}
|
||||
else if (%i > 0)
|
||||
%bin = %bin @ "0";
|
||||
}
|
||||
return %bin;
|
||||
}
|
||||
function BinToDec(%bin)
|
||||
{
|
||||
%dec = 0;
|
||||
for (%i = 0; %i < strLen(%bin); %i++)
|
||||
%dec += getSubStr(%bin, %i, 1) * mPow(2, strLen(%bin) - %i - 1);
|
||||
return %dec;
|
||||
}
|
||||
|
||||
//no length limit
|
||||
function DecToHex(%dec)
|
||||
{
|
||||
%bin = DecToBin(%dec);
|
||||
while (strLen(%bin) % 4 != 0)
|
||||
%bin = "0" @ %bin;
|
||||
|
||||
for (%i = 0; %i < strLen(%bin); %i += 4)
|
||||
{
|
||||
%block = getSubStr(%bin, strLen(%bin) - %i - 4, 4);
|
||||
%part = BinToDec(%block);
|
||||
if (%part > 9)
|
||||
{
|
||||
switch (%part)
|
||||
{
|
||||
case 10:
|
||||
%hex = "a" @ %hex;
|
||||
case 11:
|
||||
%hex = "b" @ %hex;
|
||||
case 12:
|
||||
%hex = "c" @ %hex;
|
||||
case 13:
|
||||
%hex = "d" @ %hex;
|
||||
case 14:
|
||||
%hex = "e" @ %hex;
|
||||
case 15:
|
||||
%hex = "f" @ %hex;
|
||||
}
|
||||
}
|
||||
else
|
||||
%hex = %part @ %hex;
|
||||
}
|
||||
if (strlen(%hex) == 0)
|
||||
return "00";
|
||||
else
|
||||
return %hex;
|
||||
}
|
||||
|
||||
Base64_CreateArray();
|
||||
43
docs/base/@vl2/T2csri.vl2/t2csri/certstore.rb
Normal file
43
docs/base/@vl2/T2csri.vl2/t2csri/certstore.rb
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
#
|
||||
# Tribes 2 Community System Reengineering Initiative
|
||||
# Client Side Credential/Certificate Store
|
||||
# Version 1.1 (2009/01/25)
|
||||
#
|
||||
# Written by Electricutioner/Thyth
|
||||
# http://absolous.no-ip.com/
|
||||
# Copyright 2008 - 2009
|
||||
#
|
||||
# Released under the terms of the GNU General Public License v3 or later.
|
||||
# http://www.gnu.org/licenses/gpl.html
|
||||
# Your use of this software is subject to the terms of that license. Use, modification, or distribution
|
||||
# constitutes acceptance of these software terms. This license is the only manner by which you are permitted
|
||||
# to use this software, thus rejection of the license terms prohibits your use of this software.
|
||||
#
|
||||
$accCerts = Hash.new
|
||||
$accPrivateKeys = Hash.new
|
||||
|
||||
def certstore_loadAccounts
|
||||
IO.foreach('public.store') {|line| $accCerts[line.split("\t")[0].downcase] = line.rstrip.lstrip }
|
||||
IO.foreach('private.store') {|line| $accPrivateKeys[line.split("\t")[0].downcase] = line.rstrip.lstrip }
|
||||
end
|
||||
|
||||
def certstore_addAccount(public, private)
|
||||
$accCerts[public.split("\t")[0].downcase] = public
|
||||
$accPrivateKeys[public.split("\t")[0].downcase] = private
|
||||
|
||||
publicstore = File.new('public.store', 'a')
|
||||
publicstore.seek(0, IO::SEEK_END)
|
||||
publicstore.puts(public + "\r\n")
|
||||
publicstore.close
|
||||
|
||||
privatestore = File.new('private.store', 'a')
|
||||
privatestore.seek(0, IO::SEEK_END)
|
||||
privatestore.puts(private + "\r\n")
|
||||
privatestore.close
|
||||
end
|
||||
|
||||
def certstore_listAccounts
|
||||
list = String.new
|
||||
$accCerts.each_key { |username| list = list.rstrip + "\t" + $accCerts[username].split("\t")[0].to_s }
|
||||
return list.lstrip
|
||||
end
|
||||
|
|
@ -8,12 +8,201 @@
|
|||
// load the clan support functions
|
||||
exec("t2csri/clientSideClans.cs");
|
||||
|
||||
// initialize the SHA1 digester in Ruby
|
||||
function t2csri_initDigester()
|
||||
{
|
||||
$SHA1::Initialized = 1;
|
||||
rubyEval("$sha1hasher = SHA1Pure.new");
|
||||
}
|
||||
|
||||
// use Ruby to get the SHA1 hash of the string
|
||||
function sha1sum(%string)
|
||||
{
|
||||
if (!$SHA1::Initialized)
|
||||
t2csri_initDigester();
|
||||
%string = strReplace(%string, "'", "\\'");
|
||||
rubyEval("$sha1hasher.prepare");
|
||||
rubyEval("$sha1hasher.append('" @ %string @ "')");
|
||||
rubyEval("tsEval '$temp=\"' + $sha1hasher.hexdigest + '\";'");
|
||||
%temp = $temp;
|
||||
$temp = "";
|
||||
return %temp;
|
||||
}
|
||||
|
||||
// get the password encrypted private key for the following name
|
||||
// assuming it is installed on the system
|
||||
function t2csri_getEncryptedAccountKey(%name)
|
||||
{
|
||||
return rubyGetValue("$accPrivateKeys['" @ strlwr(%name) @ "']");
|
||||
}
|
||||
|
||||
// get the public certificate key for the following name
|
||||
// assuming it is installed on the system
|
||||
function t2csri_getAccountCertificate(%name)
|
||||
{
|
||||
// check if the name exists
|
||||
%found = 0;
|
||||
for (%i = 0; %i < getFieldCount($accountList); %i++)
|
||||
{
|
||||
if (%name $= getField($accountList, %i))
|
||||
%found = 1;
|
||||
}
|
||||
|
||||
// this is a bit of a hack -- Ruby 1.9.0 has some problems getting the account on the first try
|
||||
%value = "";
|
||||
if (%found)
|
||||
{
|
||||
while (strLen(%value) == 0)
|
||||
{
|
||||
%value = rubyGetValue("$accCerts['" @ strlwr(%name) @ "']");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
%value = rubyGetValue("$accCerts['" @ strlwr(%name) @ "']");
|
||||
}
|
||||
return %value;
|
||||
}
|
||||
|
||||
// prevents a warning generated when leaving a server, and allows the yellow
|
||||
// highlight selection on the warrior screen that indicates the active account
|
||||
function WONGetAuthInfo()
|
||||
{
|
||||
$LoginCertificate = t2csri_getAccountCertificate();
|
||||
return getField($LoginCertificate, 0) @ "\t\t0\t" @ getField($LoginCertificate, 1) @ "\n";
|
||||
return getField($LoginCertificate, 0) @ "\t\t0\t" @ getField($LoginCertificate, 1) @ "\n";
|
||||
}
|
||||
|
||||
// decrypt an RC4 encrypted account key
|
||||
// also used for encryption on the plaintext when generating the account
|
||||
function t2csri_decryptAccountKey(%account, %password, %nonce, %doingEncryption)
|
||||
{
|
||||
%key = sha1sum(%password @ %nonce);
|
||||
|
||||
// initiate RC4 stream state with key
|
||||
%iterations = 256;
|
||||
for (%i = 0; %i < %iterations; %i++)
|
||||
{
|
||||
%SArray[%i] = %i;
|
||||
}
|
||||
%j = 0;
|
||||
for (%i = 0; %i < %iterations; %i++)
|
||||
{
|
||||
%j = (%j + %SArray[%i] + strCmp(getSubStr(%key, %i % strLen(%key), 1), "")) % %iterations;
|
||||
|
||||
//swap(S[i],S[j])
|
||||
%temp = %SArray[%i];
|
||||
%SArray[%i] = %SArray[%j];
|
||||
%SArray[%j] = %temp;
|
||||
}
|
||||
|
||||
// discard 2048 bytes from the start of the stream to avoid the strongly biased first bytes
|
||||
%seedI = 0; %seedJ = 0;
|
||||
for (%i = 0; %i < 2048; %i++)
|
||||
{
|
||||
%seedI = (%seedI + 1) % 256;
|
||||
%seedJ = (%seedJ + %SArray[%seedI]) % 256;
|
||||
|
||||
%temp = %SArray[%seedI];
|
||||
%SArray[%seedI] = %SArray[%seedJ];
|
||||
%SArray[%seedJ] = %temp;
|
||||
}
|
||||
|
||||
// decrypt the account
|
||||
%bytes = strlen(%account) / 2;
|
||||
for (%i = 0; %i < %bytes; %i++)
|
||||
{
|
||||
%seedI = (%seedI + 1) % 256;
|
||||
%seedJ = (%seedJ + %SArray[%seedI]) % 256;
|
||||
|
||||
%temp = %SArray[%seedI];
|
||||
%SArray[%seedI] = %SArray[%seedJ];
|
||||
%SArray[%seedJ] = %temp;
|
||||
|
||||
%schar = %SArray[(%SArray[%seedI] + %SArray[%seedJ]) % 256];
|
||||
%achar = strCmp(collapseEscape("\\x" @ getSubStr(%account, %i * 2, 2)), "");
|
||||
%byte = DecToHex(%schar ^ %achar);
|
||||
if (strLen(%byte) < 2)
|
||||
%byte = "0" @ %byte;
|
||||
%out = %out @ %byte;
|
||||
}
|
||||
|
||||
// verify that the password is correct by checking with the nonce (SHA1 plaintext hash)
|
||||
%hash = sha1sum(%out);
|
||||
if (%hash $= %nonce || %doingEncryption)
|
||||
return %out;
|
||||
else
|
||||
{
|
||||
%out = getSubStr(%out, 0, strlen(%out) - 2);
|
||||
// last 4-bit block was corrupted... try to fix it
|
||||
for (%i = 0; %i < 16; %i++)
|
||||
{
|
||||
%chunk = getSubStr(DecToHex(%i), 1, 1);
|
||||
%hash = sha1sum(%out @ %chunk);
|
||||
if (%hash $= %nonce)
|
||||
return %out @ %chunk;
|
||||
}
|
||||
// last 8-bit block was corrupted... try to fix it
|
||||
for (%i = 0; %i < 256; %i++)
|
||||
{
|
||||
%chunk = DecToHex(%i);
|
||||
%hash = sha1sum(%out @ %chunk);
|
||||
if (%hash $= %nonce)
|
||||
return %out @ %chunk;
|
||||
}
|
||||
|
||||
// looks like the password was still wrong
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
function t2csri_encryptAccountKey(%account, %password)
|
||||
{
|
||||
%nonce = sha1sum(%account);
|
||||
return %nonce @ ":" @ t2csri_decryptAccountKey(%account, %password, %nonce, 1);
|
||||
}
|
||||
|
||||
// this does the "login" process internally for accounts that exist
|
||||
// it finds the cert, the private key, decrypts it, and sets up the
|
||||
// RSA key data structures in the Ruby environment.
|
||||
function t2csri_getAccount(%username, %password)
|
||||
{
|
||||
$LoginUsername = %username;
|
||||
$LoginCertificate = t2csri_getAccountCertificate(%username);
|
||||
if ($LoginCertificate $= "")
|
||||
{
|
||||
return "NO_SUCH_ACCOUNT";
|
||||
}
|
||||
|
||||
// split the certificate into its components
|
||||
// username guid e n signature
|
||||
%user = getField($LoginCertificate, 0);
|
||||
%guid = getField($LoginCertificate, 1);
|
||||
%e = getField($LoginCertificate, 2);
|
||||
%n = getField($LoginCertificate, 3);
|
||||
%sig = getField($LoginCertificate, 4);
|
||||
|
||||
// nonce:encrypted
|
||||
%encryptedKey = t2csri_getEncryptedAccountKey(%username);
|
||||
%encryptedKey = getField(%encryptedKey, 1); // strip the username from the field
|
||||
%nonce = getSubStr(%encryptedKey, 0, strstr(%encryptedKey, ":"));
|
||||
%block = getSubStr(%encryptedKey, strLen(%nonce) + 1, strLen(%encryptedKey));
|
||||
%decryptedKey = t2csri_decryptAccountKey(%block, %password, %nonce);
|
||||
if (%decryptedKey $= "")
|
||||
{
|
||||
return "INVALID_PASSWORD";
|
||||
}
|
||||
|
||||
// we have the account, and the properly decrypted private key... interface with Ruby and
|
||||
// insert the data...
|
||||
rubyEval("$accountKey = RSAKey.new");
|
||||
rubyEval("$accountKey.e = '" @ %e @ "'.to_i(16)");
|
||||
rubyEval("$accountKey.n = '" @ %n @ "'.to_i(16)");
|
||||
rubyEval("$accountKey.d = '" @ %decryptedKey @ "'.to_i(16)");
|
||||
// protect the private exponent (d) from reading now.
|
||||
// this will prevent scripts from stealing the private exponent, but still
|
||||
// allows doing decryption using the player's account key
|
||||
rubyEval("$accountKey.protect");
|
||||
|
||||
return "SUCCESS";
|
||||
}
|
||||
|
||||
// this sends a request to the authentication server to retrieve an account that is
|
||||
|
|
@ -34,7 +223,10 @@ function t2csri_downloadAccount(%username, %password)
|
|||
//echo(%authStored);
|
||||
|
||||
// get time in UTC, use it as a nonce to prevent replay attacks
|
||||
%utc = time();
|
||||
rubyEval("tsEval '$temp=\"' + Time.new.getutc.to_s + '\";'");
|
||||
%utc = $temp;
|
||||
$temp = "";
|
||||
//echo(%utc);
|
||||
|
||||
// time/username nonce
|
||||
%timeNonce = sha1sum(%utc @ strlwr(%username));
|
||||
|
|
@ -70,13 +262,14 @@ function t2csri_processDownloadCompletion()
|
|||
%cert = getSubStr(%cert, 6, strlen(%cert));
|
||||
%exp = getField(%cert, 0) @ "\t" @ getSubStr(%exp, 5, strlen(%exp));
|
||||
// add it to the store
|
||||
t2csri_storeAccount(%cert, %exp);
|
||||
rubyEval("certstore_addAccount('" @ %cert @ "','" @ %exp @ "')");
|
||||
|
||||
// refresh the UI
|
||||
$LastLoginKey = $LoginName;
|
||||
LoginEditMenu.clear();
|
||||
LoginEditMenu.populate();
|
||||
LoginEditMenu.setActive(1);
|
||||
LoginEditMenu.setSelected(0);
|
||||
LoginEditBox.clear();
|
||||
}
|
||||
else
|
||||
|
|
@ -93,7 +286,6 @@ function t2csri_processDownloadCompletion()
|
|||
function t2csri_gameServerHexAddress()
|
||||
{
|
||||
%ip = ServerConnection.getAddress();
|
||||
|
||||
%ip = getSubStr(%ip, strstr(%ip, ":") + 1, strlen(%ip));
|
||||
%ip = getSubStr(%ip, 0, strstr(%ip, ":"));
|
||||
%ip = strReplace(%ip, ".", " ");
|
||||
|
|
@ -111,7 +303,7 @@ function t2csri_gameServerHexAddress()
|
|||
// client side interface to communicate with the game server
|
||||
function clientCmdt2csri_pokeClient(%version)
|
||||
{
|
||||
echo("T2CSRI: Authenticating with connected game server. (" @ %version @ ")");
|
||||
echo("T2CSRI: Authenticating with connected game server.");
|
||||
|
||||
// send the community certificate, assuming server is running later than 1.0
|
||||
if (getWord(%version, 1) > 1.0)
|
||||
|
|
@ -119,7 +311,6 @@ function clientCmdt2csri_pokeClient(%version)
|
|||
|
||||
$encryptedchallenge = "";
|
||||
|
||||
$LoginCertificate = t2csri_getAccountCertificate();
|
||||
// send the certificate in 200 byte parts
|
||||
for (%i = 0; %i < strlen($LoginCertificate); %i += 200)
|
||||
{
|
||||
|
|
@ -127,11 +318,10 @@ function clientCmdt2csri_pokeClient(%version)
|
|||
}
|
||||
|
||||
// send a 64 bit challenge to the server to prevent replay attacks
|
||||
$loginchallenge = rand_challenge(18446744073709551615);
|
||||
rubyEval("tsEval '$loginchallenge=\"' + rand(18446744073709551615).to_s(16) + '\";'");
|
||||
// append what the client thinks the server IP address is, for anti-replay purposes
|
||||
$loginchallenge = $loginchallenge @ t2csri_gameServerHexAddress();
|
||||
|
||||
schedule(0, 0, commandToServer, 't2csri_sendChallenge', $loginchallenge);
|
||||
commandToServer('t2csri_sendChallenge', $loginchallenge);
|
||||
|
||||
// at this point, server will validate the signature on the certificate then
|
||||
// proceed to verifying the client has the private part of the key if valid
|
||||
|
|
@ -151,7 +341,8 @@ function clientCmdt2csri_decryptChallenge()
|
|||
%challenge = strlwr($encryptedchallenge);
|
||||
for (%i = 0; %i < strlen(%challenge); %i++)
|
||||
{
|
||||
if (!isxdigit(getSubStr(%challenge, %i, 1)))
|
||||
%char = strcmp(getSubStr(%challenge, %i, 1), "");
|
||||
if ((%char < 48 || %char > 102) || (%char > 57 && %char < 97))
|
||||
{
|
||||
schedule(1000, 0, MessageBoxOK, "REJECTED","Invalid characters in server challenge.");
|
||||
disconnect();
|
||||
|
|
@ -159,11 +350,11 @@ function clientCmdt2csri_decryptChallenge()
|
|||
}
|
||||
}
|
||||
|
||||
%decryptedChallenge = t2csri_rsa_decrypt(%challenge);
|
||||
rubyEval("tsEval '$decryptedChallenge=\"' + $accountKey.decrypt('" @ %challenge @ "'.to_i(16)).to_s(16) + '\";'");
|
||||
|
||||
// verify that the client challenge is intact, and extract the server challenge
|
||||
%replayedClientChallenge = getSubStr(%decryptedChallenge, 0, strLen($loginchallenge));
|
||||
%serverChallenge = getSubStr(%decryptedChallenge, strlen(%replayedClientChallenge), strLen(%decryptedChallenge));
|
||||
%replayedClientChallenge = getSubStr($decryptedChallenge, 0, strLen($loginchallenge));
|
||||
%serverChallenge = getSubStr($decryptedChallenge, strlen(%replayedClientChallenge), strLen($decryptedChallenge));
|
||||
if (%replayedClientChallenge !$= $loginchallenge)
|
||||
{
|
||||
schedule(1000, 0, MessageBoxOK, "REJECTED","Server sent back wrong client challenge.");
|
||||
|
|
@ -186,7 +377,7 @@ function clientCmdt2csri_decryptChallenge()
|
|||
// private exponent -- different x requires different time for x^d, and d bits can be found
|
||||
// if you are really resourceful... adding this schedule kills time accuracy and makes such
|
||||
// a correlation attack very improbable
|
||||
schedule(getRandom(64, 512), 0, commandToServer, 't2csri_challengeResponse', %serverChallenge);
|
||||
schedule(getRandom(128, 512), 0, commandToServer, 't2csri_challengeResponse', %serverChallenge);
|
||||
|
||||
// at this point, server will verify that the challenge is equivalent to the one it sent encrypted
|
||||
// to the client. the only way it can be equivalent is if the client has the private key they
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@ function clientCmdt2csri_requestUnknownDCECert(%dceNum)
|
|||
{
|
||||
commandToServer('t2csri_getDCEChunk', getSubStr(%cert, %i, 200));
|
||||
}
|
||||
|
||||
commandToServer('t2csri_finishedDCE');
|
||||
}
|
||||
|
||||
|
|
@ -51,6 +50,5 @@ function t2csri_sendCommunityCert()
|
|||
{
|
||||
commandToServer('t2csri_sendCommunityCertChunk', getSubStr(%cert, %i, 200));
|
||||
}
|
||||
|
||||
commandToServer('t2csri_comCertSendDone');
|
||||
}
|
||||
|
|
|
|||
492
docs/base/@vl2/T2csri.vl2/t2csri/crypto.rb
Normal file
492
docs/base/@vl2/T2csri.vl2/t2csri/crypto.rb
Normal file
|
|
@ -0,0 +1,492 @@
|
|||
#
|
||||
# Tribes 2 Community System Reengineering Initiative
|
||||
# Assymetric Cryptography Identity Provisioning
|
||||
# Version 1.0
|
||||
#
|
||||
# Written by Electricutioner/Thyth
|
||||
# http://absolous.no-ip.com/
|
||||
# Copyright 2008
|
||||
#
|
||||
# Released under the terms of the GNU General Public License v3 or later.
|
||||
# http://www.gnu.org/licenses/gpl.html
|
||||
# Your use of this software is subject to the terms of that license. Use, modification, or distribution
|
||||
# constitutes acceptance of these software terms. This license is the only manner by which you are permitted
|
||||
# to use this software, thus rejection of the license terms prohibits your use of this software.
|
||||
#
|
||||
|
||||
# fast modular exponentiation -- the key to the RSA algorithm
|
||||
# result = (b ^ e) % m
|
||||
def rsa_mod_exp(b, e, m)
|
||||
result = 1
|
||||
while (e > 0)
|
||||
if ((e & 1) == 1)
|
||||
result = (result * b) % m
|
||||
end
|
||||
e = e >> 1
|
||||
b = (b * b) % m
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
# RSA key class to keep things nice and organized
|
||||
class RSAKey
|
||||
# allow reading and writing the key values
|
||||
attr_reader :e, :n, :twister, :strength
|
||||
attr_writer :e, :d, :n, :twister
|
||||
|
||||
# allow protecting the d value so it isn't stolen by evil scripts
|
||||
# once a key is protected, it cannot be deprotected, but it can be used to decrypt
|
||||
def protect
|
||||
@protected = 1
|
||||
end
|
||||
# attribute reader for d that returns nil if key protection is active
|
||||
def d
|
||||
if (@protected == 1)
|
||||
return nil
|
||||
else
|
||||
return @d
|
||||
end
|
||||
end
|
||||
|
||||
# encrypt a message with the public exponent (e)
|
||||
# this could be construed as a misnomer, since this is used to verify authentication
|
||||
# images from the authentication server, and to verify a client has both parts of the key they
|
||||
# claim to have
|
||||
def encrypt(message)
|
||||
rsa_mod_exp(message, @e, @n)
|
||||
end
|
||||
|
||||
# decrypt a message with the private exponent (d), also usable for signing
|
||||
# obviously, this will fail if the instance is only the public part of the key
|
||||
def decrypt(message)
|
||||
rsa_mod_exp(message, @d, @n)
|
||||
end
|
||||
|
||||
# generate a new random RSA key of the specified bitsize
|
||||
# this generates keys that should be resistant to quick factorization techniques
|
||||
def generate(bitsize)
|
||||
p = 0
|
||||
q = 0
|
||||
@n = 100
|
||||
@strength = bitsize
|
||||
|
||||
# test for some conditions that could produce insecure RSA keys
|
||||
# p, q difference to see if Fermat factorization could be successful
|
||||
# p - q must be greater than 2*(n ^ (1/4))
|
||||
while ((p - q).abs < (2 * Math.sqrt(Math.sqrt(@n))))
|
||||
p = createPrime(bitsize / 2, 150)
|
||||
q = createPrime(bitsize / 2, 150)
|
||||
@n = p * q
|
||||
end
|
||||
|
||||
totient = (p - 1) * (q - 1)
|
||||
|
||||
# e must be coprime to the totient. we start at 3 and add 2 whenever coprime test fails
|
||||
@e = 3
|
||||
coprimee = 0
|
||||
while (coprimee)
|
||||
if (@e > 7)
|
||||
# e over 7 has a large chance of not being coprime to the totient
|
||||
generate(bitsize)
|
||||
return
|
||||
end
|
||||
block = extendedEuclid(@e, totient, 0, 1, 1, 0)
|
||||
if (block[0] > 1)
|
||||
@e = @e + 2
|
||||
else
|
||||
coprimee = nil
|
||||
end
|
||||
end
|
||||
|
||||
# calculate the d value such that d * e = 1 mod totient
|
||||
# this calculation is done in the coprime of e verification
|
||||
@d = block[1]
|
||||
while (@d < 0)
|
||||
@d = @d + totient
|
||||
end
|
||||
|
||||
# verify that the generated key is a valid RSA key
|
||||
1.upto(10) do |i|
|
||||
testVal = @twister.randomnumber(bitsize) % @n
|
||||
if (decrypt(encrypt(testVal)) != testVal)
|
||||
# key failed... generate a new one
|
||||
generate(bitsize)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# private methods that people shouldn't be poking without a good reason
|
||||
private
|
||||
# obtain gcd and return the "d" value that we want
|
||||
def extendedEuclid(a, b, c, d, e, f)
|
||||
if (b == 0)
|
||||
block = Array.new(3, 0)
|
||||
block[0] = a; # gcd(a, b)
|
||||
block[1] = e; # coefficient of 'a' and the 'd' value we want
|
||||
block[2] = f; # coefficient of 'b'
|
||||
return block
|
||||
else
|
||||
return extendedEuclid(b, a % b, e - ((a / b) * c), f - ((a / b) * d), c, d);
|
||||
end
|
||||
end
|
||||
|
||||
# create a prime number of the specified bitlength
|
||||
# the number of tests specified will control how many miller-rabin primality tests are run
|
||||
# this function will return a prime number with a high degree of confidence if sufficient
|
||||
# tests are run
|
||||
def createPrime(bitlen, tests)
|
||||
# generate a random number of the specific bitlen
|
||||
p = @twister.randomnumber(bitlen)
|
||||
|
||||
# run the primality tests
|
||||
testrun = 0
|
||||
while (testrun < tests)
|
||||
if (prime?(p))
|
||||
testrun = testrun + 1
|
||||
else # not prime -- generate a new one
|
||||
return createPrime(bitlen, tests)
|
||||
end
|
||||
end
|
||||
return p
|
||||
end
|
||||
|
||||
# run a miller-rabin primality test on the given number
|
||||
# returns true if the number is "probably" prime
|
||||
def prime?(potential)
|
||||
qandm = getqm(potential)
|
||||
if (qandm[0] == -1)
|
||||
return nil
|
||||
end
|
||||
|
||||
bval = @twister.randomnumber(@strength / 2)
|
||||
mval = qandm[1]
|
||||
|
||||
if (rsa_mod_exp(bval, mval, potential) == 1)
|
||||
return 1
|
||||
end
|
||||
j = 0
|
||||
while (j < qandm[0])
|
||||
if ((potential - 1) == rsa_mod_exp(bval, mval, potential))
|
||||
return 1
|
||||
end
|
||||
mval = mval * 2
|
||||
j = j + 1
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
def getqm(p)
|
||||
p = p - 1
|
||||
rt = Array.new(2, 0)
|
||||
if (p & 1 != 0)
|
||||
rt[0] = -1
|
||||
rt[1] = -1
|
||||
return rt
|
||||
end
|
||||
div = p / 2
|
||||
counter = 1
|
||||
while (div & 1 == 0)
|
||||
counter = counter + 1
|
||||
div = div / 2
|
||||
end
|
||||
rt[0] = counter
|
||||
rt[1] = div
|
||||
return rt
|
||||
end
|
||||
end
|
||||
|
||||
# Mersenne Twister pseudo random number generator, modified for cryptographic security
|
||||
# period length should be 20 * (2 ^ 19937 - 1)
|
||||
class MersenneTwister
|
||||
@index = 0
|
||||
|
||||
# build the internal storage array
|
||||
def initialize
|
||||
@mt = Array.new(624, 0)
|
||||
end
|
||||
|
||||
# initialize the generator from a seed, can be done repeatedly
|
||||
def seedgen(seed)
|
||||
@mt[0] = seed
|
||||
1.upto(623) do |i|
|
||||
@mt[i] = 0xffffffff & (1812433243 * (@mt[i - 1] ^ (@mt[i - 1] >> 30)) + i)
|
||||
end
|
||||
generateNumbers
|
||||
end
|
||||
|
||||
# extract a number that does not give away the state of the generator, takes 37 elements from generator
|
||||
# and applies SHA1 on it to get a 20 element number. this is repeated until the required length
|
||||
# is reached, and truncated as necessary to bring it down to the requested bitlen
|
||||
def randomnumber(bits)
|
||||
bytes = bits / 8
|
||||
if (bits % 8 != 0)
|
||||
bytes = bytes + 1
|
||||
end
|
||||
|
||||
produced = 0
|
||||
output = 0
|
||||
stages = 0
|
||||
mask = 0
|
||||
|
||||
sha1hash = SHA1Pure.new
|
||||
while (produced < bytes)
|
||||
sha1hash.prepare
|
||||
1.upto(37) do |i|
|
||||
sha1hash.append(extractNumber().to_s);
|
||||
end
|
||||
digest = sha1hash.hexdigest.to_i(16)
|
||||
output = output | (digest << (160 * stages))
|
||||
produced = produced + 20
|
||||
stages = stages + 1
|
||||
end
|
||||
|
||||
0.upto(bits.to_i) do |i|
|
||||
mask = (mask.to_i << 1) | 1
|
||||
end
|
||||
return (output & mask)
|
||||
end
|
||||
|
||||
private
|
||||
# extract a tempered pseudorandom number
|
||||
def extractNumber()
|
||||
if (@index == 0)
|
||||
generateNumbers()
|
||||
end
|
||||
|
||||
y = @mt[@index.to_i]
|
||||
y = y ^ (y >> 11)
|
||||
y = y ^ ((y << 7) & 2636928640)
|
||||
y = y ^ ((y << 15) & 4022730752)
|
||||
y = y ^ (y >> 18)
|
||||
y = y & 0xffffffff
|
||||
|
||||
@index = (@index.to_i + 1) % 624
|
||||
return y
|
||||
end
|
||||
|
||||
# generate 624 untempered numbers for this generator's array
|
||||
def generateNumbers()
|
||||
0.upto(623) do |i|
|
||||
y = (@mt[i] & 0x80000000) + (@mt[(i + 1) % 624] & 0x7FFFFFFF)
|
||||
@mt[i] = @mt[(i + 397) % 624] ^ (y >> 1)
|
||||
if (y & 1 == 1)
|
||||
@mt[i] = @mt[i] ^ 2567483615
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# SHA1 in Pure Ruby
|
||||
class SHA1Pure
|
||||
|
||||
def initialize
|
||||
prepare
|
||||
end
|
||||
|
||||
# prepare the hash digester for a new hash
|
||||
def prepare
|
||||
@state = Array.new(5, 0)
|
||||
@block = Array.new(16, 0)
|
||||
@blockIndex = 0
|
||||
@count = 0
|
||||
|
||||
@state[0] = 0x67452301
|
||||
@state[1] = 0xefcdab89
|
||||
@state[2] = 0x98badcfe
|
||||
@state[3] = 0x10325476
|
||||
@state[4] = 0xc3d2e1f0
|
||||
end
|
||||
|
||||
# append a string to the string being digested
|
||||
def append(str)
|
||||
str = str.to_s
|
||||
str.each_byte {|c| update(c.to_i & 0xff)}
|
||||
end
|
||||
|
||||
# produce a hexidecimal digest string
|
||||
def hexdigest
|
||||
bits = Array.new(8, 0)
|
||||
0.upto(7) do |i|
|
||||
bits[i] = (@count >> (((7 - i) * 8) & 0xff)) & 0xff
|
||||
end
|
||||
update(128)
|
||||
while (@blockIndex != 56)
|
||||
update(0)
|
||||
end
|
||||
0.upto(7) do |i|
|
||||
update(bits[i])
|
||||
end # this will accomplish a transform
|
||||
|
||||
# output the digest
|
||||
digest = ""
|
||||
0.upto(4) do |i|
|
||||
chunk = @state[i].to_s(16)
|
||||
while(chunk.length < 8)
|
||||
chunk = "0" + chunk
|
||||
end
|
||||
digest = digest + chunk
|
||||
end
|
||||
prepare
|
||||
return digest
|
||||
end
|
||||
|
||||
private
|
||||
def rol(val, bits)
|
||||
val = val.to_i
|
||||
bits = bits.to_i
|
||||
return (val << bits) | (val >> (32 - bits))
|
||||
end
|
||||
|
||||
def blk0(i)
|
||||
i = i.to_i
|
||||
@block[i] = (rol(@block[i], 24) & 0xff00ff00) | (rol(@block[i], 8) & 0xff00ff)
|
||||
@block[i] = @block[i] & 0xffffffff
|
||||
return @block[i]
|
||||
end
|
||||
|
||||
def blk(i)
|
||||
i = i.to_i
|
||||
@block[i & 15] = rol(@block[(i + 13) & 15] ^ @block[(i + 8) & 15] ^ @block[(i + 2) & 15] ^ @block[i & 15], 1)
|
||||
@block[i & 15] = @block[i & 15] & 0xffffffff
|
||||
return @block[i & 15]
|
||||
end
|
||||
|
||||
def r0(data, v, w, x, y, z, i)
|
||||
data[z] += ((data[w] & (data[x] ^ data[y])) ^ data[y]) + blk0(i) + 0x5a827999 + rol(data[v], 5)
|
||||
data[z] = data[z] & 0xffffffff
|
||||
data[w] = rol(data[w], 30) & 0xffffffff
|
||||
end
|
||||
|
||||
def r1(data, v, w, x, y, z, i)
|
||||
data[z] += ((data[w] & (data[x] ^ data[y])) ^ data[y]) + blk(i) + 0x5a827999 + rol(data[v], 5)
|
||||
data[z] = data[z] & 0xffffffff
|
||||
data[w] = rol(data[w], 30) & 0xffffffff
|
||||
end
|
||||
|
||||
def r2(data, v, w, x, y, z, i)
|
||||
data[z] += (data[w] ^ data[x] ^ data[y]) + blk(i) + 0x6ed9eba1 + rol(data[v], 5)
|
||||
data[z] = data[z] & 0xffffffff
|
||||
data[w] = rol(data[w], 30) & 0xffffffff
|
||||
end
|
||||
|
||||
def r3(data, v, w, x, y, z, i)
|
||||
data[z] += (((data[w] | data[x]) & data[y]) | (data[w] & data[x])) + blk(i) + 0x8f1bbcdc + rol(data[v], 5)
|
||||
data[z] = data[z] & 0xffffffff
|
||||
data[w] = rol(data[w], 30) & 0xffffffff
|
||||
end
|
||||
|
||||
def r4(data, v, w, x, y, z, i)
|
||||
data[z] += (data[w] ^ data[x] ^ data[y]) + blk(i) + 0xca62c1d6 + rol(data[v], 5)
|
||||
data[z] = data[z] & 0xffffffff
|
||||
data[w] = rol(data[w], 30) & 0xffffffff
|
||||
end
|
||||
|
||||
def transform
|
||||
dd = Array.new(5, 0)
|
||||
dd[0] = @state[0]
|
||||
dd[1] = @state[1]
|
||||
dd[2] = @state[2]
|
||||
dd[3] = @state[3]
|
||||
dd[4] = @state[4]
|
||||
|
||||
r0(dd,0,1,2,3,4, 0)
|
||||
r0(dd,4,0,1,2,3, 1)
|
||||
r0(dd,3,4,0,1,2, 2)
|
||||
r0(dd,2,3,4,0,1, 3)
|
||||
r0(dd,1,2,3,4,0, 4)
|
||||
r0(dd,0,1,2,3,4, 5)
|
||||
r0(dd,4,0,1,2,3, 6)
|
||||
r0(dd,3,4,0,1,2, 7)
|
||||
r0(dd,2,3,4,0,1, 8)
|
||||
r0(dd,1,2,3,4,0, 9)
|
||||
r0(dd,0,1,2,3,4,10)
|
||||
r0(dd,4,0,1,2,3,11)
|
||||
r0(dd,3,4,0,1,2,12)
|
||||
r0(dd,2,3,4,0,1,13)
|
||||
r0(dd,1,2,3,4,0,14)
|
||||
r0(dd,0,1,2,3,4,15)
|
||||
r1(dd,4,0,1,2,3,16)
|
||||
r1(dd,3,4,0,1,2,17)
|
||||
r1(dd,2,3,4,0,1,18)
|
||||
r1(dd,1,2,3,4,0,19)
|
||||
r2(dd,0,1,2,3,4,20)
|
||||
r2(dd,4,0,1,2,3,21)
|
||||
r2(dd,3,4,0,1,2,22)
|
||||
r2(dd,2,3,4,0,1,23)
|
||||
r2(dd,1,2,3,4,0,24)
|
||||
r2(dd,0,1,2,3,4,25)
|
||||
r2(dd,4,0,1,2,3,26)
|
||||
r2(dd,3,4,0,1,2,27)
|
||||
r2(dd,2,3,4,0,1,28)
|
||||
r2(dd,1,2,3,4,0,29)
|
||||
r2(dd,0,1,2,3,4,30)
|
||||
r2(dd,4,0,1,2,3,31)
|
||||
r2(dd,3,4,0,1,2,32)
|
||||
r2(dd,2,3,4,0,1,33)
|
||||
r2(dd,1,2,3,4,0,34)
|
||||
r2(dd,0,1,2,3,4,35)
|
||||
r2(dd,4,0,1,2,3,36)
|
||||
r2(dd,3,4,0,1,2,37)
|
||||
r2(dd,2,3,4,0,1,38)
|
||||
r2(dd,1,2,3,4,0,39)
|
||||
r3(dd,0,1,2,3,4,40)
|
||||
r3(dd,4,0,1,2,3,41)
|
||||
r3(dd,3,4,0,1,2,42)
|
||||
r3(dd,2,3,4,0,1,43)
|
||||
r3(dd,1,2,3,4,0,44)
|
||||
r3(dd,0,1,2,3,4,45)
|
||||
r3(dd,4,0,1,2,3,46)
|
||||
r3(dd,3,4,0,1,2,47)
|
||||
r3(dd,2,3,4,0,1,48)
|
||||
r3(dd,1,2,3,4,0,49)
|
||||
r3(dd,0,1,2,3,4,50)
|
||||
r3(dd,4,0,1,2,3,51)
|
||||
r3(dd,3,4,0,1,2,52)
|
||||
r3(dd,2,3,4,0,1,53)
|
||||
r3(dd,1,2,3,4,0,54)
|
||||
r3(dd,0,1,2,3,4,55)
|
||||
r3(dd,4,0,1,2,3,56)
|
||||
r3(dd,3,4,0,1,2,57)
|
||||
r3(dd,2,3,4,0,1,58)
|
||||
r3(dd,1,2,3,4,0,59)
|
||||
r4(dd,0,1,2,3,4,60)
|
||||
r4(dd,4,0,1,2,3,61)
|
||||
r4(dd,3,4,0,1,2,62)
|
||||
r4(dd,2,3,4,0,1,63)
|
||||
r4(dd,1,2,3,4,0,64)
|
||||
r4(dd,0,1,2,3,4,65)
|
||||
r4(dd,4,0,1,2,3,66)
|
||||
r4(dd,3,4,0,1,2,67)
|
||||
r4(dd,2,3,4,0,1,68)
|
||||
r4(dd,1,2,3,4,0,69)
|
||||
r4(dd,0,1,2,3,4,70)
|
||||
r4(dd,4,0,1,2,3,71)
|
||||
r4(dd,3,4,0,1,2,72)
|
||||
r4(dd,2,3,4,0,1,73)
|
||||
r4(dd,1,2,3,4,0,74)
|
||||
r4(dd,0,1,2,3,4,75)
|
||||
r4(dd,4,0,1,2,3,76)
|
||||
r4(dd,3,4,0,1,2,77)
|
||||
r4(dd,2,3,4,0,1,78)
|
||||
r4(dd,1,2,3,4,0,79)
|
||||
|
||||
@state[0] = (@state[0] + dd[0]) & 0xffffffff
|
||||
@state[1] = (@state[1] + dd[1]) & 0xffffffff
|
||||
@state[2] = (@state[2] + dd[2]) & 0xffffffff
|
||||
@state[3] = (@state[3] + dd[3]) & 0xffffffff
|
||||
@state[4] = (@state[4] + dd[4]) & 0xffffffff
|
||||
end
|
||||
|
||||
def update(b)
|
||||
mask = (8 * (@blockIndex & 3))
|
||||
@count = @count + 8
|
||||
@block[@blockIndex >> 2] = @block[@blockIndex >> 2] & ~(0xff << mask)
|
||||
@block[@blockIndex >> 2] = @block[@blockIndex >> 2] | ((b & 0xff) << mask)
|
||||
@blockIndex = @blockIndex + 1
|
||||
if (@blockIndex == 64)
|
||||
transform
|
||||
@blockIndex = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -11,8 +11,17 @@
|
|||
// load the torque script components
|
||||
exec("t2csri/authconnect.cs");
|
||||
exec("t2csri/authinterface.cs");
|
||||
exec("t2csri/base64.cs");
|
||||
exec("t2csri/clientSide.cs");
|
||||
exec("t2csri/ipv4.cs");
|
||||
exec("t2csri/rubyUtils.cs");
|
||||
|
||||
// load the Ruby components
|
||||
rubyExec("t2csri/crypto.rb");
|
||||
rubyExec("t2csri/certstore.rb");
|
||||
|
||||
rubyEval("certstore_loadAccounts");
|
||||
rubyEval("tsEval '$RubyEnabled=1;'");
|
||||
|
||||
// connect to the auth server via signed lookup
|
||||
schedule(32, 0, authConnect_findAuthServer);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// Tribes 2 Unofficial Authentication System
|
||||
// http://www.tribesnext.com/
|
||||
// Written by Electricutioner/Thyth
|
||||
// Copyright 2008 by Electricutioner/Thyth and the Tribes 2 Community System Reengineering Intitiative
|
||||
|
||||
// IPv4 Utils Version 1.1 (03/26/2008)
|
||||
|
|
@ -10,7 +11,7 @@
|
|||
// when the game launches, so there shouldn't be more than a
|
||||
// couple of hundred hits per day from the entire T2 community.
|
||||
|
||||
$IPv4::AutomationURL = "/whatismyip";
|
||||
$IPv4::AutomationURL = "/whatismyip.php";
|
||||
|
||||
function ipv4_getInetAddress()
|
||||
{
|
||||
|
|
@ -22,20 +23,20 @@ function ipv4_getInetAddress()
|
|||
IPv4Connection.disconnect();
|
||||
IPv4Connection.delete();
|
||||
}
|
||||
new TCPObject(IPv4Connection);
|
||||
IPV4Connection.data = "GET " @ $IPv4::AutomationURL @ " HTTP/1.1\r\nHost: www.tribesnext.com\r\nUser-Agent: Tribes 2\r\nConnection: close\r\n\r\n";
|
||||
IPv4Connection.connect("www.tribesnext.com:80");
|
||||
}
|
||||
|
||||
new HTTPObject(IPv4Connection)
|
||||
{
|
||||
enableIPv6 = false;
|
||||
};
|
||||
|
||||
IPv4Connection.get("master.tribesnext.com", $IPv4::AutomationURL);
|
||||
function IPv4Connection::onConnected(%this)
|
||||
{
|
||||
%this.send(%this.data);
|
||||
}
|
||||
|
||||
function IPv4Connection::onLine(%this, %line)
|
||||
{
|
||||
if (%line $= "" || %line == 0)
|
||||
return;
|
||||
|
||||
$IPv4::InetAddress = %line;
|
||||
%this.disconnect();
|
||||
}
|
||||
|
|
@ -93,12 +94,13 @@ function ipv4_reasonableConnection(%source, %destination)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// convert a (big endian) hex block into a numeric IP
|
||||
function ipv4_hexBlockToIP(%hex)
|
||||
{
|
||||
for (%i = 0; %i < 4; %i++)
|
||||
{
|
||||
%ip = %ip @ "." @ ord(collapseEscape("\\x" @ getSubStr(%hex, %i * 2, 2)));
|
||||
%ip = %ip @ "." @ strcmp(collapseEscape("\\x" @ getSubStr(%hex, %i * 2, 2)), "");
|
||||
}
|
||||
return getSubStr(%ip, 1, strlen(%ip) - 1);
|
||||
}
|
||||
|
|
|
|||
31
docs/base/@vl2/T2csri.vl2/t2csri/rubyUtils.cs
Normal file
31
docs/base/@vl2/T2csri.vl2/t2csri/rubyUtils.cs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
// Tribes 2 Unofficial Authentication System
|
||||
// http://www.tribesnext.com/
|
||||
// Written by Electricutioner/Thyth
|
||||
// Copyright 2008-2009 by Electricutioner/Thyth and the Tribes 2 Community System Reengineering Intitiative
|
||||
|
||||
// Ruby Interface Utilities Version 1.3 (01/27/2009)
|
||||
|
||||
// loads a ruby script
|
||||
function rubyExec(%script)
|
||||
{
|
||||
echo("Loading Ruby script " @ %script @ ".");
|
||||
new FileObject("RubyExecutor");
|
||||
RubyExecutor.openForRead(%script);
|
||||
|
||||
while (!RubyExecutor.isEOF())
|
||||
{
|
||||
%line = RubyExecutor.readLine();
|
||||
%buffer = %buffer @ "\n" @ %line;
|
||||
}
|
||||
rubyEval(%buffer);
|
||||
RubyExecutor.close();
|
||||
RubyExecutor.delete();
|
||||
}
|
||||
|
||||
// extracts a value from the Ruby interpreter environment
|
||||
function rubyGetValue(%value)
|
||||
{
|
||||
$temp = "";
|
||||
rubyEval("tsEval '$temp=\"' + " @ %value @ " + '\";'");
|
||||
return $temp;
|
||||
}
|
||||
|
|
@ -1,10 +1,41 @@
|
|||
// Tribes 2 Unofficial Authentication System
|
||||
// http://www.tribesnext.com/
|
||||
// Written by Electricutioner/Thyth
|
||||
// Copyright 2008 by Electricutioner/Thyth and the Tribes 2 Community System Reengineering Intitiative
|
||||
|
||||
// Version 1.3: 2009-04-23
|
||||
// Version 1.2: 2009-02-16
|
||||
// Clan/Rename Certificate support is included in this version.
|
||||
|
||||
// initialize the SHA1 digester in Ruby
|
||||
function t2csri_initDigester()
|
||||
{
|
||||
$SHA1::Initialized = 1;
|
||||
rubyEval("$sha1hasher = SHA1Pure.new");
|
||||
}
|
||||
|
||||
// use Ruby to get the SHA1 hash of the string
|
||||
function sha1sum(%string)
|
||||
{
|
||||
if (!$SHA1::Initialized)
|
||||
t2csri_initDigester();
|
||||
%string = strReplace(%string, "'", "\\'");
|
||||
rubyEval("$sha1hasher.prepare");
|
||||
rubyEval("$sha1hasher.append('" @ %string @ "')");
|
||||
rubyEval("tsEval '$temp=\"' + $sha1hasher.hexdigest + '\";'");
|
||||
%temp = $temp;
|
||||
$temp = "";
|
||||
return %temp;
|
||||
}
|
||||
|
||||
// verify with the auth server's RSA public key... hard coded in the executable
|
||||
function t2csri_verify_auth_signature(%sig)
|
||||
{
|
||||
rubyEval("tsEval '$temp=\"' + t2csri_verify_auth_signature('" @ %sig @ "').to_s(16) + '\";'");
|
||||
while (strLen($temp) < 40)
|
||||
$temp = "0" @ $temp;
|
||||
return $temp;
|
||||
}
|
||||
|
||||
// server sends the client a certificate in chunks, since they can be rather large
|
||||
function serverCmdt2csri_sendCertChunk(%client, %chunk)
|
||||
{
|
||||
|
|
@ -16,7 +47,7 @@ function serverCmdt2csri_sendCertChunk(%client, %chunk)
|
|||
if (strlen(%client.t2csri_cert) > 20000)
|
||||
{
|
||||
%client.setDisconnectReason("Account certificate too long. Check your account key for corruption.");
|
||||
%client.schedule(0, delete);
|
||||
%client.delete();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -46,43 +77,20 @@ function serverCmdt2csri_sendChallenge(%client, %clientChallenge)
|
|||
if (%client.doneAuthenticating)
|
||||
return;
|
||||
|
||||
if (%client.t2csri_retryChallenge)
|
||||
{
|
||||
if (isEventPending(%client.t2csri_retryChallenge))
|
||||
cancel(%client.t2csri_retryChallenge);
|
||||
|
||||
%client.t2csri_retryChallenge = "";
|
||||
}
|
||||
|
||||
if (!%client.t2csri_sentComCertDone && strLen(%client.t2csri_comCert) > 0)
|
||||
{
|
||||
%client.t2csri_retryChallenge = schedule(250, 0, serverCmdt2csri_sendChallenge, %client, %clientChallenge);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strlen(%client.t2csri_cert) < 1024)
|
||||
{
|
||||
%client.setDisconnectReason("Invalid authentication certificate.");
|
||||
%client.schedule(0, delete);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// echo("Client requesting challenge. CC: " @ %clientChallenge);
|
||||
// echo("Client's certificate: " @ %client.t2csri_cert);
|
||||
//echo("Client requesting challenge. CC: " @ %clientChallenge);
|
||||
//echo("Client's certificate: " @ %client.t2csri_cert);
|
||||
// verify that the certificate the client sent is signed by the authentication server
|
||||
%user = strReplace(getField(%client.t2csri_cert, 0), "\x27", "\\\x27");
|
||||
|
||||
%guid = getField(%client.t2csri_cert, 1);
|
||||
|
||||
// sanitize GUID
|
||||
for (%i = 0; %i < strlen(%guid); %i++)
|
||||
{
|
||||
%char = ord(getSubStr(%guid, %i, 1));
|
||||
%char = strcmp(getSubStr(%guid, %i, 1), "");
|
||||
if (%char > 57 || %char < 48)
|
||||
{
|
||||
%client.setDisconnectReason("Invalid characters in client GUID.");
|
||||
%client.schedule(0, delete);
|
||||
%client.delete();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -95,10 +103,11 @@ function serverCmdt2csri_sendChallenge(%client, %clientChallenge)
|
|||
%rsa_chunk = strlwr(%e @ %n @ %sig);
|
||||
for (%i = 0; %i < strlen(%rsa_chunk); %i++)
|
||||
{
|
||||
if (!isxdigit(getSubStr(%rsa_chunk, %i, 1)))
|
||||
%char = strcmp(getSubStr(%rsa_chunk, %i, 1), "");
|
||||
if ((%char < 48 || %char > 102) || (%char > 57 && %char < 97))
|
||||
{
|
||||
%client.setDisconnectReason("Invalid characters in certificate RSA fields.");
|
||||
%client.schedule(0, delete);
|
||||
%client.delete();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -106,16 +115,11 @@ function serverCmdt2csri_sendChallenge(%client, %clientChallenge)
|
|||
// get a SHA1 sum
|
||||
%sumStr = %user @ "\t" @ %guid @ "\t" @ %e @ "\t" @ %n;
|
||||
%certSum = sha1sum(%sumStr);
|
||||
|
||||
while (strLen(%sig) < 1024)
|
||||
%sig = "0" @ %sig;
|
||||
|
||||
%verifSum = t2csri_verify_auth_signature(%sig);
|
||||
while (strLen(%verifSum) < 40)
|
||||
%verifSum = "0" @ %verifSum;
|
||||
|
||||
// echo("Calc'd SHA1: " @ %certSum);
|
||||
// echo("Signed SHA1: " @ %verifSum);
|
||||
//echo("Calc'd SHA1: " @ %certSum);
|
||||
//echo("Signed SHA1: " @ %verifSum);
|
||||
|
||||
// verify signature
|
||||
if (%verifSum !$= %certSum)
|
||||
|
|
@ -123,7 +127,7 @@ function serverCmdt2csri_sendChallenge(%client, %clientChallenge)
|
|||
// client supplied a bogus certificate that was never signed by the auth server
|
||||
// abort their connection
|
||||
%client.setDisconnectReason("Invalid account certificate.");
|
||||
%client.schedule(0, delete);
|
||||
%client.delete();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -135,10 +139,11 @@ function serverCmdt2csri_sendChallenge(%client, %clientChallenge)
|
|||
%clientChallenge = strlwr(%clientChallenge);
|
||||
for (%i = 0; %i < strlen(%clientChallenge); %i++)
|
||||
{
|
||||
if (!isxdigit(getSubStr(%clientChallenge, %i, 1)))
|
||||
%char = strcmp(getSubStr(%clientChallenge, %i, 1), "");
|
||||
if ((%char < 48 || %char > 102) || (%char > 57 && %char < 97))
|
||||
{
|
||||
%client.setDisconnectReason("Invalid characters in client challenge.");
|
||||
%client.schedule(0, delete);
|
||||
%client.delete();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -150,25 +155,22 @@ function serverCmdt2csri_sendChallenge(%client, %clientChallenge)
|
|||
if (!ipv4_reasonableConnection(%sourceIP, %sanityIP))
|
||||
{
|
||||
%client.setDisconnectReason("Potential man in the middle attack detected. Your client claims it connected to: " @ %sanityIP @ ", but the server does not consider this reasonable.");
|
||||
%client.schedule(0, delete);
|
||||
%client.delete();
|
||||
return;
|
||||
}
|
||||
|
||||
// calculate a random 64-bit server side challenge
|
||||
%client.t2csri_serverChallenge = rand_challenge() @ t2csri_gameClientHexAddress(%client);
|
||||
rubyEval("tsEval '$temp=\"' + rand(18446744073709551615).to_s(16) + '\";'");
|
||||
%client.t2csri_serverChallenge = $temp @ t2csri_gameClientHexAddress(%client);
|
||||
|
||||
%fullChallenge = %client.t2csri_clientChallenge @ %client.t2csri_serverChallenge;
|
||||
if (strlen(%fullChallenge) % 2)
|
||||
%fullChallenge = "0" @ %fullChallenge;
|
||||
|
||||
%temp = rsa_mod_exp(%fullChallenge, %e, %n);
|
||||
rubyEval("tsEval '$temp=\"' + rsa_mod_exp('" @ %fullChallenge @ "'.to_i(16), '" @ %e @ "'.to_i(16), '" @ %n @ "'.to_i(16)).to_s(16) + '\";'");
|
||||
|
||||
// send the challenge in 200 byte chunks
|
||||
for (%i = 0; %i < strlen(%temp); %i += 200)
|
||||
for (%i = 0; %i < strlen($temp); %i += 200)
|
||||
{
|
||||
commandToClient(%client, 't2csri_getChallengeChunk', getSubStr(%temp, %i, 200));
|
||||
commandToClient(%client, 't2csri_getChallengeChunk', getSubStr($temp, %i, 200));
|
||||
}
|
||||
|
||||
// tell the client we're done sending
|
||||
commandToClient(%client, 't2csri_decryptChallenge');
|
||||
|
||||
|
|
@ -189,7 +191,7 @@ function serverCmdt2csri_sendChallenge(%client, %clientChallenge)
|
|||
{
|
||||
// uh oh... someone's being naughty.. valid cert, but for a different player. kill them!
|
||||
%client.setDisconnectReason("Community supplemental certificate doesn't match account certificate.");
|
||||
%client.schedule(0, delete);
|
||||
%client.delete();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -206,10 +208,10 @@ function serverCmdt2csri_challengeResponse(%client, %serverChallenge)
|
|||
if (%client.t2csri_serverChallenge $= %serverChallenge)
|
||||
{
|
||||
// check to see if the client is GUID banned, now that we verified their certificate
|
||||
if (BanList::isBanned(getField(%client.t2csri_authInfo, 3), "*"))
|
||||
if (banList_checkGUID(getField(%client.t2csri_authInfo, 3)))
|
||||
{
|
||||
%client.setDisconnectReason("You are not allowed to play on this server.");
|
||||
%client.schedule(0, delete);
|
||||
%client.delete();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -219,7 +221,7 @@ function serverCmdt2csri_challengeResponse(%client, %serverChallenge)
|
|||
else
|
||||
{
|
||||
%client.setDisconnectReason("Invalid server challenge. Check your account key for corruption.");
|
||||
%client.schedule(0, delete);
|
||||
%client.delete();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -228,9 +230,8 @@ function t2csri_expireClient(%client)
|
|||
{
|
||||
if (!isObject(%client))
|
||||
return;
|
||||
|
||||
%client.setDisconnectReason("This is a TribesNEXT server. You must install the TribesNEXT client to play. See www.tribesnext.com for info.");
|
||||
%client.schedule(0, delete);
|
||||
%client.setDisconnectReason("This is a TribesNext server. You must install the TribesNext client to play. See www.tribesnext.com for info.");
|
||||
%client.delete();
|
||||
}
|
||||
|
||||
package t2csri_server
|
||||
|
|
@ -238,17 +239,17 @@ package t2csri_server
|
|||
// packaged to create the "pre-connection" authentication phase
|
||||
function GameConnection::onConnect(%client, %name, %raceGender, %skin, %voice, %voicePitch)
|
||||
{
|
||||
if (%client.getAddress() !$= "local" && %client.t2csri_serverChallenge $= "")
|
||||
if (%client.t2csri_serverChallenge $= "" && !%client.isAIControlled() && %client.getAddress() !$= "Local")
|
||||
{
|
||||
// check to see if the client is IP banned
|
||||
if (BanList::isBanned(0, %client.getAddress()))
|
||||
if (banList_checkIP(%client))
|
||||
{
|
||||
%client.setDisconnectReason("You are not allowed to play on this server.");
|
||||
%client.schedule(0, delete);
|
||||
%client.delete();
|
||||
return;
|
||||
}
|
||||
|
||||
// echo("Client connected. Initializing pre-connection authentication phase...");
|
||||
//echo("Client connected. Initializing pre-connection authentication phase...");
|
||||
// save these for later
|
||||
%client.tname = %name;
|
||||
%client.trgen = %raceGender;
|
||||
|
|
@ -259,10 +260,10 @@ package t2csri_server
|
|||
// start the 15 second count down
|
||||
%client.tterm = schedule(15000, 0, t2csri_expireClient, %client);
|
||||
|
||||
commandToClient(%client, 't2csri_pokeClient', "T2CSRI 1.5 - 08/09/2012");
|
||||
commandToClient(%client, 't2csri_pokeClient', "T2CSRI 1.1 - 03/18/2009");
|
||||
return;
|
||||
}
|
||||
// echo("Client completed pre-authentication phase.");
|
||||
//echo("Client completed pre-authentication phase.");
|
||||
|
||||
// continue connection process
|
||||
if (isEventPending(%client.tterm))
|
||||
|
|
@ -270,7 +271,6 @@ package t2csri_server
|
|||
|
||||
Parent::onConnect(%client, %name, %raceGender, %skin, %voice, %voicePitch);
|
||||
%client.doneAuthenticating = 1;
|
||||
%client.t2csri_cert = "";
|
||||
}
|
||||
|
||||
// packaged to prevent game leaving messages for clients that are in the authentication phase
|
||||
|
|
@ -278,7 +278,6 @@ package t2csri_server
|
|||
{
|
||||
if (!isObject(%client) || !%client.doneAuthenticating)
|
||||
return;
|
||||
|
||||
Parent::onDrop(%client, %reason);
|
||||
}
|
||||
|
||||
|
|
@ -292,33 +291,11 @@ package t2csri_server
|
|||
// clan support will be implemented via delegation to a community server
|
||||
function GameConnection::getAuthInfo(%client)
|
||||
{
|
||||
if (%client.t2csri_authInfo $= "" && %client.getAddress() $= "local")
|
||||
if (%client.getAddress() $= "Local" && %client.t2csri_authInfo $= "")
|
||||
%client.t2csri_authInfo = WONGetAuthInfo();
|
||||
|
||||
return %client.t2csri_authInfo;
|
||||
}
|
||||
|
||||
// deactivating old master list server protocol handlers in script
|
||||
// sending a game type list to a dedicated server would result in a massive number
|
||||
// of nuiscance calls to the following functions, and spam the console with pages of errors
|
||||
// the errors were the main source of CPU utilization, so just setting stubs is adequate protection
|
||||
function addGameType()
|
||||
{
|
||||
return;
|
||||
}
|
||||
function clearGameTypes()
|
||||
{
|
||||
return;
|
||||
}
|
||||
function clearMissionTypes()
|
||||
{
|
||||
return;
|
||||
}
|
||||
function sortGameAndMissionTypeLists()
|
||||
{
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if ($PlayingOnline && !isActivePackage(t2csri_server))
|
||||
activatePackage(t2csri_server);
|
||||
if ($PlayingOnline)
|
||||
activatePackage(t2csri_server);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// Tribes 2 Unofficial Authentication System
|
||||
// http://www.tribesnext.com/
|
||||
// Written by Electricutioner/Thyth
|
||||
// Copyright 2008 by Electricutioner/Thyth and the Tribes 2 Community System Reengineering Intitiative
|
||||
|
||||
// Version 1.0: 2009-02-13
|
||||
|
|
@ -24,6 +25,16 @@
|
|||
// HexBlob format:
|
||||
// (Follows same format as contents returned by getAuthInfo, but is hex encoded.)
|
||||
|
||||
// verify with the delegation RSA public key... hard coded in the executable
|
||||
function t2csri_verify_deleg_signature(%sig)
|
||||
{
|
||||
%sig = strReplace(%sig, "\x27", "\\\x27");
|
||||
rubyEval("tsEval '$temp=\"' + t2csri_verify_deleg_signature('" @ %sig @ "').to_s(16) + '\";'");
|
||||
while (strLen($temp) < 40)
|
||||
$temp = "0" @ $temp;
|
||||
return $temp;
|
||||
}
|
||||
|
||||
// allow the client to send in an unknown DCE certificate
|
||||
function serverCmdt2csri_getDCEChunk(%client, %chunk)
|
||||
{
|
||||
|
|
@ -77,6 +88,8 @@ function serverCmdt2csri_finishedDCE(%client)
|
|||
|
||||
if (%sigSha !$= %calcSha)
|
||||
{
|
||||
echo(%sigSha);
|
||||
warn(%calcSha);
|
||||
%client.setDisconnectReason("DCE is not signed by authoritative root.");
|
||||
%client.delete();
|
||||
return;
|
||||
|
|
@ -161,9 +174,10 @@ function serverCmdt2csri_comCertSendDone(%client)
|
|||
}
|
||||
|
||||
// get the signature SHA1
|
||||
%sigSha = rsa_mod_exp(%sig, %e, %n);
|
||||
while (strlen(%sigSha) < 40)
|
||||
%sigSha = "0" @ %sigSha;
|
||||
rubyEval("tsEval '$temp = \"' + rsa_mod_exp('" @ %sig @ "'.to_i(16), '" @ %e @ "'.to_i(16), '" @ %n @ "'.to_i(16)).to_s(16) + '\";'");
|
||||
while (strlen($temp) < 40)
|
||||
$temp = "0" @ $temp;
|
||||
%sigSha = $temp;
|
||||
|
||||
if (%sigSha !$= %calcSha)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,16 +1,22 @@
|
|||
// Tribes 2 Unofficial Authentication System
|
||||
// http://www.tribesnext.com/
|
||||
// Written by Electricutioner/Thyth
|
||||
// Copyright 2008 by Electricutioner/Thyth and the Tribes 2 Community System Reengineering Intitiative
|
||||
|
||||
// Version 1.0 initialization and glue file (server side)
|
||||
|
||||
if (isObject(ServerGroup))
|
||||
{
|
||||
// load the Ruby utils and cryptography module
|
||||
exec("t2csri/rubyUtils.cs");
|
||||
rubyExec("t2csri/crypto.rb");
|
||||
|
||||
// load the torque script components
|
||||
exec("t2csri/serverSide.cs");
|
||||
exec("t2csri/serverSideClans.cs");
|
||||
exec("t2csri/bans.cs");
|
||||
exec("t2csri/ipv4.cs");
|
||||
exec("t2csri/base64.cs");
|
||||
|
||||
// get the global IP for sanity testing purposes
|
||||
schedule(32, 0, ipv4_getInetAddress);
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue