Compare commits
17 Commits
v0.2.2
...
fc29111fe1
| Author | SHA1 | Date | |
|---|---|---|---|
| fc29111fe1 | |||
| 5b49e5d584 | |||
| 55aac72bd2 | |||
| b10639316e | |||
| d782832fc5 | |||
| b25cd1a039 | |||
| e269dfc208 | |||
| 066f8fbea5 | |||
| 4a3daf7d8c | |||
| 13ad4e08d2 | |||
| 7e35cf0c31 | |||
| 18d43f9481 | |||
| 5662d22481 | |||
| a996fe7b3c | |||
| ecb8825734 | |||
| d4edf049ed | |||
| d1f4ec0266 |
40
.dockerignore
Normal file
40
.dockerignore
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
# Node modules
|
||||||
|
node_modules
|
||||||
|
**/node_modules
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
logs
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# Build outputs
|
||||||
|
build
|
||||||
|
dist
|
||||||
|
out
|
||||||
|
.next
|
||||||
|
.cache
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# Environment files
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
|
||||||
|
# OS files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# IDE / Editor folders
|
||||||
|
.vscode
|
||||||
|
.idea
|
||||||
|
*.sublime-workspace
|
||||||
|
*.sublime-project
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
*.swp
|
||||||
|
*.bak
|
||||||
|
*.tmp
|
||||||
146
.drone.yml
Normal file
146
.drone.yml
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: default
|
||||||
|
|
||||||
|
platform:
|
||||||
|
os: linux
|
||||||
|
arch: arm64
|
||||||
|
|
||||||
|
workspace:
|
||||||
|
path: /drone/src
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- name: dockersock
|
||||||
|
host:
|
||||||
|
path: /var/run/docker.sock
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: fetch-tags
|
||||||
|
image: docker:24
|
||||||
|
volumes:
|
||||||
|
- name: dockersock
|
||||||
|
path: /var/run/docker.sock
|
||||||
|
commands:
|
||||||
|
- apk add --no-cache git
|
||||||
|
- git fetch --tags
|
||||||
|
- |
|
||||||
|
# Get latest Git tag and trim newline
|
||||||
|
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null | tr -d '\n')
|
||||||
|
echo "Latest Git tag fetched: $LATEST_TAG"
|
||||||
|
|
||||||
|
# Save to file for downstream steps
|
||||||
|
echo "$LATEST_TAG" > /drone/src/LATEST_TAG.txt
|
||||||
|
|
||||||
|
# Read back for verification
|
||||||
|
IMAGE_TAG=$(cat /drone/src/LATEST_TAG.txt | tr -d '\n')
|
||||||
|
echo "Image tag read from file: $IMAGE_TAG"
|
||||||
|
|
||||||
|
# Validate
|
||||||
|
if [ -z "$IMAGE_TAG" ]; then
|
||||||
|
echo "❌ No git tags found! Cannot continue."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: check-remote-image
|
||||||
|
image: docker:24
|
||||||
|
volumes:
|
||||||
|
- name: dockersock
|
||||||
|
path: /var/run/docker.sock
|
||||||
|
commands:
|
||||||
|
- IMAGE_TAG=$(cat /drone/src/LATEST_TAG.txt | tr -d '\n')
|
||||||
|
|
||||||
|
- echo "Checking if lila-games/tic-tac-toe-ui:$IMAGE_TAG exists on remote Docker..."
|
||||||
|
- echo "Existing Docker tags for lila-games/tic-tac-toe-ui:"
|
||||||
|
- docker images --format "{{.Repository}}:{{.Tag}}" | grep "^lila-games/tic-tac-toe-ui" || echo "(none)"
|
||||||
|
- |
|
||||||
|
if docker image inspect lila-games/tic-tac-toe-ui:$IMAGE_TAG > /dev/null 2>&1; then
|
||||||
|
echo "✅ Docker image lila-games/tic-tac-toe-ui:$IMAGE_TAG already exists — skipping build"
|
||||||
|
exit 78
|
||||||
|
else
|
||||||
|
echo "⚙️ Docker image lila-games/tic-tac-toe-ui:$IMAGE_TAG not found — proceeding to build..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: build-image
|
||||||
|
image: docker:24
|
||||||
|
environment:
|
||||||
|
WS_HOST:
|
||||||
|
from_secret: WS_HOST
|
||||||
|
WS_PORT:
|
||||||
|
from_secret: WS_PORT
|
||||||
|
WS_SKEY:
|
||||||
|
from_secret: WS_SKEY
|
||||||
|
WS_SSL:
|
||||||
|
from_secret: WS_SSL
|
||||||
|
volumes:
|
||||||
|
- name: dockersock
|
||||||
|
path: /var/run/docker.sock
|
||||||
|
commands:
|
||||||
|
- IMAGE_TAG=$(cat /drone/src/LATEST_TAG.txt | tr -d '\n')
|
||||||
|
|
||||||
|
- echo "🔨 Building Docker image lila-games/tic-tac-toe-ui:$IMAGE_TAG ..."
|
||||||
|
- |
|
||||||
|
docker build --network=host \
|
||||||
|
--build-arg VITE_WS_HOST="$WS_HOST" \
|
||||||
|
--build-arg VITE_WS_PORT="$WS_PORT" \
|
||||||
|
--build-arg VITE_WS_SKEY="$WS_SKEY" \
|
||||||
|
--build-arg VITE_WS_SSL="$WS_SSL" \
|
||||||
|
-t lila-games/tic-tac-toe-ui:$IMAGE_TAG \
|
||||||
|
-t lila-games/tic-tac-toe-ui:latest \
|
||||||
|
/drone/src
|
||||||
|
|
||||||
|
- name: push-image
|
||||||
|
image: docker:24
|
||||||
|
environment:
|
||||||
|
REGISTRY_HOST:
|
||||||
|
from_secret: REGISTRY_HOST
|
||||||
|
REGISTRY_USER:
|
||||||
|
from_secret: REGISTRY_USER
|
||||||
|
REGISTRY_PASS:
|
||||||
|
from_secret: REGISTRY_PASS
|
||||||
|
volumes:
|
||||||
|
- name: dockersock
|
||||||
|
path: /var/run/docker.sock
|
||||||
|
commands:
|
||||||
|
- IMAGE_TAG=$(cat /drone/src/LATEST_TAG.txt | tr -d '\n')
|
||||||
|
|
||||||
|
- echo "🔑 Logging into registry $REGISTRY_HOST ..."
|
||||||
|
- echo "$REGISTRY_PASS" | docker login $REGISTRY_HOST -u "$REGISTRY_USER" --password-stdin
|
||||||
|
- echo "🏷️ Tagging images with registry prefix..."
|
||||||
|
- docker tag lila-games/tic-tac-toe-ui:$IMAGE_TAG $REGISTRY_HOST/lila-games/tic-tac-toe-ui:$IMAGE_TAG
|
||||||
|
- docker tag lila-games/tic-tac-toe-ui:$IMAGE_TAG $REGISTRY_HOST/lila-games/tic-tac-toe-ui:latest
|
||||||
|
- echo "📤 Pushing lila-games/tic-tac-toe-ui:$IMAGE_TAG ..."
|
||||||
|
- docker push $REGISTRY_HOST/lila-games/tic-tac-toe-ui:$IMAGE_TAG
|
||||||
|
- echo "📤 Pushing lila-games/tic-tac-toe-ui:latest ..."
|
||||||
|
- docker push $REGISTRY_HOST/lila-games/tic-tac-toe-ui:latest
|
||||||
|
|
||||||
|
- name: stop-old
|
||||||
|
image: docker:24
|
||||||
|
volumes:
|
||||||
|
- name: dockersock
|
||||||
|
path: /var/run/docker.sock
|
||||||
|
commands:
|
||||||
|
- echo "🛑 Stopping old container..."
|
||||||
|
- docker rm -f tic-tac-toe-ui || true
|
||||||
|
|
||||||
|
- name: run-container
|
||||||
|
image: docker:24
|
||||||
|
volumes:
|
||||||
|
- name: dockersock
|
||||||
|
path: /var/run/docker.sock
|
||||||
|
commands:
|
||||||
|
- IMAGE_TAG=$(cat /drone/src/LATEST_TAG.txt | tr -d '\n')
|
||||||
|
|
||||||
|
- echo "🚀 Starting container lila-games/tic-tac-toe-ui:$IMAGE_TAG ..."
|
||||||
|
- |
|
||||||
|
docker run -d \
|
||||||
|
--name tic-tac-toe-ui \
|
||||||
|
-p 3003:3000 \
|
||||||
|
-e NODE_ENV=production \
|
||||||
|
--restart always \
|
||||||
|
lila-games/tic-tac-toe-ui:$IMAGE_TAG
|
||||||
|
|
||||||
|
# Trigger rules
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- tag
|
||||||
42
Dockerfile
Normal file
42
Dockerfile
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# Stage 1: Build
|
||||||
|
FROM node:20-alpine AS builder
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy package.json and package-lock.json (or yarn.lock)
|
||||||
|
COPY package*.json ./
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN npm ci
|
||||||
|
|
||||||
|
# Copy the rest of the app
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Build arguments
|
||||||
|
ARG VITE_WS_HOST
|
||||||
|
ARG VITE_WS_PORT
|
||||||
|
ARG VITE_WS_SKEY
|
||||||
|
ARG VITE_WS_SSL
|
||||||
|
|
||||||
|
# Export them as actual environment variables (Vite needs ENV)
|
||||||
|
ENV VITE_WS_HOST=${VITE_WS_HOST}
|
||||||
|
ENV VITE_WS_PORT=${VITE_WS_PORT}
|
||||||
|
ENV VITE_WS_SSL=${VITE_WS_SSL}
|
||||||
|
|
||||||
|
# Build
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Stage 2: Static file server (BusyBox)
|
||||||
|
FROM busybox:latest
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy only build frontend files
|
||||||
|
COPY --from=builder /app/dist /app
|
||||||
|
|
||||||
|
# Expose port
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
# Default command
|
||||||
|
CMD ["busybox", "httpd", "-f", "-p", "3000"]
|
||||||
230
README.md
230
README.md
@@ -1,41 +1,225 @@
|
|||||||
# Material UI - React Router example in TypeScript
|
# tic-tac-toe-ui — Multiplayer Game Client (React + TypeScript + Vite)
|
||||||
|
|
||||||
## How to use
|
A fully functional multiplayer Tic-Tac-Toe game client built using **React + TypeScript**, powered by **Nakama WebSocket real-time networking**, and delivered as a tiny **production-optimized Vite build** (served via BusyBox/Docker).
|
||||||
|
|
||||||
Download the example [or clone the repo](https://github.com/mui/material-ui):
|
This UI communicates with the authoritative backend (`tic-tac-toe`) to deliver a secure, synced, cheat-proof multiplayer experience.
|
||||||
|
|
||||||
<!-- #target-branch-reference -->
|
---
|
||||||
|
|
||||||
```bash
|
## 🎮 Overview
|
||||||
curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/material-ui-react-router-ts
|
|
||||||
cd material-ui-react-router-ts
|
This repository contains the front-end implementation of the Tic-Tac-Toe multiplayer platform.
|
||||||
|
|
||||||
|
The client supports:
|
||||||
|
|
||||||
|
* Device-based authentication
|
||||||
|
* Full matchmaking lifecycle
|
||||||
|
* Real-time gameplay with WebSockets
|
||||||
|
* Authoritative state rendering
|
||||||
|
* Leaderboard browsing
|
||||||
|
* Game result screens
|
||||||
|
* A Vite-powered environment system for dynamic host/SSL selection
|
||||||
|
|
||||||
|
This UI is production-ready and deployable to any server or container environment.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⭐ Features
|
||||||
|
|
||||||
|
* **React + TypeScript UI**
|
||||||
|
* **WebSocket real-time gameplay** using Nakama JS
|
||||||
|
* **Matchmaking flow:** queue → ticket → match → gameplay
|
||||||
|
* **Authoritative state updates** (OpCode 2)
|
||||||
|
* **Secure device authentication** (device UUID → session)
|
||||||
|
* **Leaderboard view** over Nakama's leaderboard API
|
||||||
|
* **Production Docker image:** Node → Vite → BusyBox
|
||||||
|
* **Environment-based configuration** for host/SSL
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧩 Architecture
|
||||||
|
|
||||||
|
### Frontend System Diagram
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
User[Browser] --> UI[React + TS + Vite]
|
||||||
|
UI -->|WebSocket| Nakama
|
||||||
|
UI -->|HTTP| Nakama
|
||||||
|
UI --> Leaderboard[Leaderboard API]
|
||||||
|
UI --> Matchmaking[Matchmaker API]
|
||||||
```
|
```
|
||||||
|
|
||||||
Install it and run:
|
---
|
||||||
|
|
||||||
```bash
|
## 🛠 Tech Stack
|
||||||
npm install
|
|
||||||
npm run dev
|
* **React 18** (TypeScript)
|
||||||
|
* **Vite.js** (build system)
|
||||||
|
* **Nakama JavaScript Client**
|
||||||
|
* **Plain CSS** for styling
|
||||||
|
* **WebSockets (SSL / non-SSL selectable)**
|
||||||
|
* **Docker (multi-stage build)**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
## 🔧 Environment Variables (Vite)
|
||||||
|
|
||||||
|
These are injected at build time:
|
||||||
|
|
||||||
|
```
|
||||||
|
VITE_WS_HOST=nakama.aetoskia.com
|
||||||
|
VITE_WS_PORT=443
|
||||||
|
VITE_WS_SKEY=secret
|
||||||
|
VITE_WS_SSL=true
|
||||||
```
|
```
|
||||||
|
|
||||||
or:
|
Meaning:
|
||||||
|
|
||||||
<!-- #target-branch-reference -->
|
* **VITE_WS_HOST** → Nakama host (domain or IP)
|
||||||
|
* **VITE_WS_PORT** → Port for WebSocket/API
|
||||||
|
* **VITE_WS_SKEY** → Nakama server key
|
||||||
|
* **VITE_WS_SSL** → `true` for wss://, `false` for ws://
|
||||||
|
|
||||||
[](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/master/examples/material-ui-react-router-ts)
|
---
|
||||||
|
|
||||||
[](https://stackblitz.com/github/mui/material-ui/tree/master/examples/material-ui-react-router-ts)
|
## 🔌 Runtime Flow
|
||||||
|
|
||||||
## The idea behind the example
|
### Authentication
|
||||||
|
|
||||||
<!-- #host-reference -->
|
* UI generates a device UUID
|
||||||
|
* Calls `client.authenticateDevice()`
|
||||||
|
* Stores session in React state
|
||||||
|
|
||||||
This example demonstrates how you can use Material UI with [React Router](https://reactrouter.com/) in [TypeScript](https://github.com/Microsoft/TypeScript).
|
### Matchmaking
|
||||||
It includes `@mui/material` and its peer dependencies, including [Emotion](https://emotion.sh/docs/introduction), the default style engine in Material UI.
|
|
||||||
|
|
||||||
## What's next?
|
1. User selects mode (classic / blitz)
|
||||||
|
2. joins the matchmaking queue
|
||||||
|
3. Waits for matchmaker ticket
|
||||||
|
4. Auto-joins the match when assigned
|
||||||
|
|
||||||
<!-- #host-reference -->
|
### Gameplay
|
||||||
|
|
||||||
You now have a working example project.
|
* User sends moves via OpCode **1**
|
||||||
You can head back to the documentation and continue by browsing the [templates](https://mui.com/material-ui/getting-started/templates/) section.
|
* Server validates + broadcasts authoritative board via OpCode **2**
|
||||||
|
* UI re-renders board state from server packets
|
||||||
|
|
||||||
|
### End of Game
|
||||||
|
|
||||||
|
* Player sees win/lose/draw
|
||||||
|
* Can return to home or matchmaking
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎨 Styling
|
||||||
|
|
||||||
|
Styling uses **plain CSS** via a single `styles.css` file.
|
||||||
|
Simple, responsive layout using Flexbox.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🐳 Docker (Production Build)
|
||||||
|
|
||||||
|
### Dockerfile Overview
|
||||||
|
|
||||||
|
```
|
||||||
|
# Stage 1: Build
|
||||||
|
FROM node:20-alpine AS builder
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy package.json and package-lock.json (or yarn.lock)
|
||||||
|
COPY package*.json ./
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN npm ci
|
||||||
|
|
||||||
|
# Copy the rest of the app
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Build arguments
|
||||||
|
ARG VITE_WS_HOST
|
||||||
|
ARG VITE_WS_PORT
|
||||||
|
ARG VITE_WS_SKEY
|
||||||
|
ARG VITE_WS_SSL
|
||||||
|
|
||||||
|
# Export them as actual environment variables (Vite needs ENV)
|
||||||
|
ENV VITE_WS_HOST=${VITE_WS_HOST}
|
||||||
|
ENV VITE_WS_PORT=${VITE_WS_PORT}
|
||||||
|
ENV VITE_WS_SSL=${VITE_WS_SSL}
|
||||||
|
|
||||||
|
# Build
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Stage 2: Static file server (BusyBox)
|
||||||
|
FROM busybox:latest
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy only build frontend files
|
||||||
|
COPY --from=builder /app/dist /app
|
||||||
|
|
||||||
|
# Expose port
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
# Default command
|
||||||
|
CMD ["busybox", "httpd", "-f", "-p", "3000"]
|
||||||
|
```
|
||||||
|
|
||||||
|
Produces an extremely lightweight production image.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧪 Testing
|
||||||
|
|
||||||
|
Manual testing validated:
|
||||||
|
|
||||||
|
* Full matchmaking loop
|
||||||
|
* Game state sync
|
||||||
|
* Invalid move handling (server rejections)
|
||||||
|
* Disconnect behaviour
|
||||||
|
* Leaderboard retrieval
|
||||||
|
|
||||||
|
Pending:
|
||||||
|
|
||||||
|
* Stress tests
|
||||||
|
* Mobile responsiveness
|
||||||
|
* Reconnect logic
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📈 Deployment
|
||||||
|
|
||||||
|
### Supported:
|
||||||
|
|
||||||
|
* Docker on any Linux host
|
||||||
|
* Raspberry Pi (ARM)
|
||||||
|
* Google Cloud Run / Compute Engine
|
||||||
|
* Traefik reverse proxy via `games.aetoskia.com`
|
||||||
|
|
||||||
|
### Example Deployment via Docker
|
||||||
|
|
||||||
|
```
|
||||||
|
docker run -d \
|
||||||
|
-p 3003:3003 \
|
||||||
|
--restart always \
|
||||||
|
tic-tac-toe-ui:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
Traefik HTTPS routes:
|
||||||
|
|
||||||
|
* **games.aetoskia.com** → UI
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🗺️ Roadmap
|
||||||
|
|
||||||
|
* Rematch flow
|
||||||
|
* Reconnect/resume after refresh
|
||||||
|
* Improved animations
|
||||||
|
* Mobile UI redesign
|
||||||
|
* Centralized error handling
|
||||||
|
|
||||||
|
---
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap"
|
href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap"
|
||||||
/>
|
/>
|
||||||
<title>Blog - Aetoskia</title>
|
<title>TicTacToe - Aetoskia</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "tictactoe-vite",
|
"name": "tictactoe-vite",
|
||||||
"version": "v0.2.0",
|
"version": "v1.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export default function Player({
|
|||||||
);
|
);
|
||||||
const [selectedMode, setSelectedMode] = useState("classic");
|
const [selectedMode, setSelectedMode] = useState("classic");
|
||||||
const [isQueueing, setIsQueueing] = useState(false);
|
const [isQueueing, setIsQueueing] = useState(false);
|
||||||
|
const isRegistered = localStorage.getItem("registered") === "yes";
|
||||||
|
|
||||||
// ------------------------------------------
|
// ------------------------------------------
|
||||||
// CONNECT
|
// CONNECT
|
||||||
@@ -86,7 +87,7 @@ export default function Player({
|
|||||||
<input
|
<input
|
||||||
placeholder="Enter username"
|
placeholder="Enter username"
|
||||||
value={username}
|
value={username}
|
||||||
disabled={username.length > 0}
|
disabled={isRegistered}
|
||||||
onChange={(e) => setUsername(e.target.value)}
|
onChange={(e) => setUsername(e.target.value)}
|
||||||
style={{
|
style={{
|
||||||
padding: "10px",
|
padding: "10px",
|
||||||
|
|||||||
@@ -46,8 +46,20 @@ export interface NakamaContextType {
|
|||||||
export const NakamaContext = createContext<NakamaContextType>(null!);
|
export const NakamaContext = createContext<NakamaContextType>(null!);
|
||||||
|
|
||||||
export function NakamaProvider({ children }: { children: React.ReactNode }) {
|
export function NakamaProvider({ children }: { children: React.ReactNode }) {
|
||||||
|
console.log(
|
||||||
|
"[Nakama] Initializing...",
|
||||||
|
// import.meta.env.VITE_WS_SKEY,
|
||||||
|
import.meta.env.VITE_WS_HOST,
|
||||||
|
import.meta.env.VITE_WS_PORT,
|
||||||
|
import.meta.env.VITE_WS_SSL === "true"
|
||||||
|
);
|
||||||
const [client] = useState(
|
const [client] = useState(
|
||||||
() => new Client("defaultkey", "127.0.0.1", "7350")
|
() => new Client(
|
||||||
|
import.meta.env.VITE_WS_SKEY,
|
||||||
|
import.meta.env.VITE_WS_HOST,
|
||||||
|
import.meta.env.VITE_WS_PORT,
|
||||||
|
import.meta.env.VITE_WS_SSL === "true"
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const [session, setSession] = useState<Session | null>(null);
|
const [session, setSession] = useState<Session | null>(null);
|
||||||
@@ -112,8 +124,10 @@ export function NakamaProvider({ children }: { children: React.ReactNode }) {
|
|||||||
const newSession = await getSession(username);
|
const newSession = await getSession(username);
|
||||||
setSession(newSession);
|
setSession(newSession);
|
||||||
|
|
||||||
// create a socket (new Nakama 3.x signature)
|
const s = client.createSocket(
|
||||||
const s = client.createSocket(undefined, undefined); // no SSL on localhost
|
import.meta.env.VITE_WS_SSL === "true",
|
||||||
|
undefined
|
||||||
|
);
|
||||||
await s.connect(newSession, true);
|
await s.connect(newSession, true);
|
||||||
setSocket(s);
|
setSocket(s);
|
||||||
socketRef.current = s;
|
socketRef.current = s;
|
||||||
|
|||||||
5
src/vite-env.d.ts
vendored
5
src/vite-env.d.ts
vendored
@@ -1,7 +1,10 @@
|
|||||||
/// <reference types="vite/client" />
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
interface ImportMetaEnv {
|
interface ImportMetaEnv {
|
||||||
readonly VITE_WS_BASE_URL: string;
|
readonly VITE_WS_HOST: string;
|
||||||
|
readonly VITE_WS_PORT: string;
|
||||||
|
readonly VITE_WS_SKEY: string;
|
||||||
|
readonly VITE_WS_SSL: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ImportMeta {
|
interface ImportMeta {
|
||||||
|
|||||||
Reference in New Issue
Block a user