mirror of
https://github.com/BeamMP/BeamMP-Website.git
synced 2026-07-13 10:13:58 +00:00
Compare commits
76 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e98f946367 | |||
| e5c15422c1 | |||
| e511b0a179 | |||
| 847bcf9bac | |||
| 6b584bdfbf | |||
| be79e4303d | |||
| 01281ebed2 | |||
| 74adf15820 | |||
| 19cbd9da15 | |||
| c7db28166f | |||
| 8fa3ed49be | |||
| 33764c3ede | |||
| 017c8708d8 | |||
| d2ec932153 | |||
| 38213e1a50 | |||
| d879a2995f | |||
| c2e37eea5a | |||
| 13e36d5bc3 | |||
| b2cbc7239b | |||
| f203dfcbf5 | |||
| 58540763d2 | |||
| 1b9d2a0b87 | |||
| fa3059c09e | |||
| 7a840ac233 | |||
| 0322221529 | |||
| 9066e31c55 | |||
| d19a10c08b | |||
| a90e60ef32 | |||
| 070fb38709 | |||
| ed1cb0e2a8 | |||
| bedcd0cb50 | |||
| 2987de52ec | |||
| aaf888f182 | |||
| 052da9316c | |||
| 842e229e55 | |||
| b2742428d0 | |||
| 48717eeabd | |||
| 5f14414055 | |||
| a253919575 | |||
| 4626a69185 | |||
| 44268d076d | |||
| c2e460b408 | |||
| c827c81d28 | |||
| 5f9aad80ac | |||
| be9bae93de | |||
| 5295ec664b | |||
| f59b5df887 | |||
| cf3c3790b5 | |||
| d24ec58185 | |||
| a3f47c5b12 | |||
| d5c33e56e5 | |||
| 266c1470ba | |||
| 6fcf1d1f6f | |||
| 30eb6b96e0 | |||
| ebb7611a92 | |||
| 77c71a501f | |||
| c5988f94b0 | |||
| d1150ed84d | |||
| 5dfb7b79d7 | |||
| 19abd94069 | |||
| c0f96b35de | |||
| dedc16fb73 | |||
| f8d4478d35 | |||
| 738721119e | |||
| b8a5db6d48 | |||
| cc48fe2ffc | |||
| a307a3cd9f | |||
| 9a2a10499d | |||
| 3daa559a67 | |||
| e459538f18 | |||
| 0065093ca3 | |||
| 391f634d65 | |||
| 838a33b4d5 | |||
| 647ab8bda9 | |||
| 8b37e854d8 | |||
| 23ec17460e |
@@ -1,14 +1,12 @@
|
|||||||
name: Build Docker image and push to release
|
name: Build Docker image and push to release
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
release:
|
||||||
# Sequence of patterns matched against refs/tags
|
types: [published]
|
||||||
tags:
|
|
||||||
- "v*" # Push events to matching v*, i.e. v1.0, v20.15.10
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
docker:
|
docker:
|
||||||
if: 'github.event.release'
|
if: "!github.event.release.prerelease"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Connect to Tailscale
|
- name: Connect to Tailscale
|
||||||
@@ -17,11 +15,6 @@ jobs:
|
|||||||
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
|
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
|
||||||
oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}
|
oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}
|
||||||
tags: tag:ci
|
tags: tag:ci
|
||||||
- name: Install registry CA certificate
|
|
||||||
run: |
|
|
||||||
sudo mkdir -p /etc/docker/certs.d/${{ secrets.REGISTRY_URL }}
|
|
||||||
echo "${{ secrets.REGISTRY_CA_CERT }}" | base64 -d | sudo tee /etc/docker/certs.d/${{ secrets.REGISTRY_URL }}/ca.crt
|
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v3
|
uses: docker/setup-qemu-action@v3
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
@@ -38,4 +31,4 @@ jobs:
|
|||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ secrets.REGISTRY_URL }}/beammp/website:${{ github.REF_NAME }}, ${{ secrets.REGISTRY_URL }}/beammp/website:prod
|
tags: ${{ secrets.REGISTRY_URL }}/beammp/website:${{ github.REF_NAME }}, ${{ secrets.REGISTRY_URL }}/beammp/website:latest, ${{ secrets.REGISTRY_URL }}/beammp/website:production
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
name: Build Docker image and push to release
|
name: Build Docker image and push to prerelease
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
release:
|
||||||
# Sequence of patterns matched against refs/tags
|
types: [published]
|
||||||
tags:
|
|
||||||
- "v*" # Push events to matching v*, i.e. v1.0, v20.15.10
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
docker:
|
docker:
|
||||||
if: 'github.event.prerelease'
|
if: github.event.release.prerelease
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Connect to Tailscale
|
- name: Connect to Tailscale
|
||||||
@@ -17,11 +15,6 @@ jobs:
|
|||||||
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
|
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
|
||||||
oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}
|
oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}
|
||||||
tags: tag:ci
|
tags: tag:ci
|
||||||
- name: Install registry CA certificate
|
|
||||||
run: |
|
|
||||||
sudo mkdir -p /etc/docker/certs.d/${{ secrets.REGISTRY_URL }}
|
|
||||||
echo "${{ secrets.REGISTRY_CA_CERT }}" | base64 -d | sudo tee /etc/docker/certs.d/${{ secrets.REGISTRY_URL }}/ca.crt
|
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v3
|
uses: docker/setup-qemu-action@v3
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
@@ -38,4 +31,4 @@ jobs:
|
|||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ secrets.REGISTRY_URL }}/beammp/website:${{ github.REF_NAME }}, ${{ secrets.REGISTRY_URL }}/beammp/website:dev
|
tags: ${{ secrets.REGISTRY_URL }}/beammp/website:${{ github.REF_NAME }}, ${{ secrets.REGISTRY_URL }}/beammp/website:latest
|
||||||
|
|||||||
+1
-1
@@ -102,7 +102,6 @@ dist
|
|||||||
|
|
||||||
# TernJS port file
|
# TernJS port file
|
||||||
.tern-port
|
.tern-port
|
||||||
*.exe
|
|
||||||
*.zip
|
*.zip
|
||||||
|
|
||||||
# Logs
|
# Logs
|
||||||
@@ -129,3 +128,4 @@ frontend/*.ntvs*
|
|||||||
frontend/*.njsproj
|
frontend/*.njsproj
|
||||||
frontend/*.sln
|
frontend/*.sln
|
||||||
frontend/*.sw?
|
frontend/*.sw?
|
||||||
|
.DS_Store
|
||||||
|
|||||||
+11
-4
@@ -1,22 +1,29 @@
|
|||||||
# Step 1: Build stage
|
# Step 1: Build stage
|
||||||
FROM node:22-alpine3.21 AS build
|
FROM node:lts-alpine AS build
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY package*.json ./
|
COPY package*.json ./
|
||||||
RUN npm ci
|
RUN npm ci
|
||||||
COPY . .
|
COPY . .
|
||||||
ENV NODE_ENV=development
|
ENV NODE_ENV=production
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
# Step 2: Serve stage
|
# Step 2: Serve stage
|
||||||
FROM nginx:alpine
|
FROM nginx:stable
|
||||||
|
|
||||||
|
# Remove default nginx static assets
|
||||||
|
RUN rm -rf /usr/share/nginx/html/*
|
||||||
|
|
||||||
# Copy built files from the previous stage
|
# Copy built files from the previous stage
|
||||||
COPY --from=build /app/dist /usr/share/nginx/html
|
COPY --from=build /app/dist /usr/share/nginx/html
|
||||||
|
|
||||||
# Add a custom Nginx configuration
|
# Copy secure nginx configs
|
||||||
|
COPY nginx.main.conf /etc/nginx/nginx.conf
|
||||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
|
# Use non-root user for security
|
||||||
|
USER nginx
|
||||||
|
|
||||||
# Expose port 80
|
# Expose port 80
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
services:
|
||||||
|
website:
|
||||||
|
build:
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: website
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:3000:80"
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
<link rel="icon" type="image/ico+xml" href="/favicon.ico" />
|
<link rel="icon" type="image/ico+xml" href="/favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>BeamMP Website</title>
|
<title>BeamMP Website</title>
|
||||||
|
<script defer src="https://analytics.beammp.com/script.js" data-website-id="9f9e912d-3470-4048-9f11-5dd360b701e7" data-performance="true"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|||||||
+39
-8
@@ -2,21 +2,52 @@ server {
|
|||||||
listen 80;
|
listen 80;
|
||||||
|
|
||||||
server_name _;
|
server_name _;
|
||||||
|
server_tokens off;
|
||||||
|
|
||||||
root /usr/share/nginx/html;
|
root /usr/share/nginx/html;
|
||||||
|
|
||||||
index index.html;
|
index index.html;
|
||||||
|
|
||||||
location / {
|
# Baseline security hardening for a static SPA.
|
||||||
# SPA fallback: serve index.html for non-file routes so Vue Router can render NotFound
|
# NOTE: add_header directives are NOT inherited by child location blocks that
|
||||||
try_files $uri $uri/ /index.html;
|
# define their own add_header. To avoid silently dropping security headers,
|
||||||
|
# use the `expires` directive (not add_header Cache-Control) in location blocks.
|
||||||
|
add_header X-Frame-Options "DENY" always;
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||||
|
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
|
||||||
|
add_header Content-Security-Policy "default-src 'self'; img-src 'self' data: https:; script-src 'self' https://analytics.beammp.com; style-src 'self' 'unsafe-inline'; font-src 'self' data:; connect-src 'self' https://backend.beammp.com https://analytics.beammp.com; object-src 'none'; base-uri 'self'; frame-ancestors 'none'" always;
|
||||||
|
|
||||||
|
# Installer directory: serve files directly; return 404 for anything missing.
|
||||||
|
location ^~ /installer/ {
|
||||||
|
try_files $uri =404;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Let real 404s for assets return 404s; Vue handles route-level 404 via the SPA fallback above.
|
# Vite-hashed assets (JS, CSS, fonts, images): serve directly with a long cache.
|
||||||
|
# These files have content hashes in their names so stale cache is never an issue.
|
||||||
location /static/ {
|
# Return 404 if a file is genuinely missing rather than silently serving index.html.
|
||||||
# Serve static files directly
|
location ~* \.(js|css|woff2?|ttf|eot|svg|webp|avif|png|jpg|jpeg|gif|ico|map)$ {
|
||||||
expires max;
|
try_files $uri =404;
|
||||||
|
expires 1y;
|
||||||
access_log off;
|
access_log off;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Data / download files: serve directly, no client-side cache, 404 if missing.
|
||||||
|
location ~* \.(json|txt|exe|zip)$ {
|
||||||
|
try_files $uri =404;
|
||||||
|
expires -1;
|
||||||
|
access_log off;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Legacy Keymaster paths moved permanently to the dedicated domain.
|
||||||
|
# Match both localized and non-localized URLs.
|
||||||
|
location ~* "^/([a-z]{2}/)?(keymaster|k/dashboard)/?$" {
|
||||||
|
return 301 https://keymaster.beammp.com/;
|
||||||
|
}
|
||||||
|
|
||||||
|
# SPA fallback: all other paths go to index.html so Vue Router can render the
|
||||||
|
# correct view (including the NotFound page for unknown routes).
|
||||||
|
location / {
|
||||||
|
try_files $uri /index.html;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
worker_processes auto;
|
||||||
|
pid /tmp/nginx.pid;
|
||||||
|
error_log /tmp/error.log warn;
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
include /etc/nginx/mime.types;
|
||||||
|
default_type application/octet-stream;
|
||||||
|
|
||||||
|
access_log /tmp/access.log;
|
||||||
|
|
||||||
|
sendfile on;
|
||||||
|
keepalive_timeout 65;
|
||||||
|
server_tokens off;
|
||||||
|
|
||||||
|
# Redefine temp paths to writable tmpfs locations
|
||||||
|
client_body_temp_path /tmp/client_temp;
|
||||||
|
proxy_temp_path /tmp/proxy_temp;
|
||||||
|
fastcgi_temp_path /tmp/fastcgi_temp;
|
||||||
|
uwsgi_temp_path /tmp/uwsgi_temp;
|
||||||
|
scgi_temp_path /tmp/scgi_temp;
|
||||||
|
|
||||||
|
include /etc/nginx/conf.d/*.conf;
|
||||||
|
}
|
||||||
Generated
+603
-506
File diff suppressed because it is too large
Load Diff
+4
-2
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "beammp-website",
|
"name": "beammp-website",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "2.4.15",
|
"version": "2.5.2",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
@@ -11,11 +11,13 @@
|
|||||||
"format": "prettier --write \"src/**/*.{js,vue,css,json}\""
|
"format": "prettier --write \"src/**/*.{js,vue,css,json}\""
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@tailwindcss/typography": "^0.5.20",
|
||||||
"@tailwindcss/vite": "^4.1.17",
|
"@tailwindcss/vite": "^4.1.17",
|
||||||
"@vueuse/core": "^14.1.0",
|
"@vueuse/core": "^14.1.0",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"lucide-vue-next": "^0.555.0",
|
"lucide-vue-next": "^0.555.0",
|
||||||
|
"marked": "^18.0.5",
|
||||||
"reka-ui": "^2.7.0",
|
"reka-ui": "^2.7.0",
|
||||||
"tailwind-merge": "^3.4.0",
|
"tailwind-merge": "^3.4.0",
|
||||||
"tailwindcss": "^4.1.17",
|
"tailwindcss": "^4.1.17",
|
||||||
@@ -33,6 +35,6 @@
|
|||||||
"eslint-plugin-vue": "^10.6.2",
|
"eslint-plugin-vue": "^10.6.2",
|
||||||
"prettier": "^3.7.3",
|
"prettier": "^3.7.3",
|
||||||
"tw-animate-css": "^1.4.0",
|
"tw-animate-css": "^1.4.0",
|
||||||
"vite": "^7.2.4"
|
"vite": "^8.0.16"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,155 @@
|
|||||||
|
# Privacy Policy
|
||||||
|
|
||||||
|
**Last updated:** 18th June 2026
|
||||||
|
|
||||||
|
This Privacy Policy describes how BeamMP Ltd. (“we”, “us”, or “our”) collects, uses, and protects information when you use the BeamMP website, accounts, forum, mod, server, launcher, and related online services (collectively, the “**Services**”).
|
||||||
|
|
||||||
|
By accessing or using our Services, you agree to the collection and use of information in accordance with this policy.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Information We Collect
|
||||||
|
|
||||||
|
### 1.1 Information You Provide
|
||||||
|
When you register or interact with our Services, we may collect:
|
||||||
|
- Email address
|
||||||
|
- Username and profile information you choose to provide
|
||||||
|
- Content you post (topics, replies, messages, reports)
|
||||||
|
- Communications you send to moderators or administrators
|
||||||
|
- Chat messages and logs
|
||||||
|
|
||||||
|
### 1.2 Automatically Collected Information
|
||||||
|
When you access the Services, we may automatically collect:
|
||||||
|
- IP address
|
||||||
|
- Browser type and version
|
||||||
|
- Operating system
|
||||||
|
- Date and time of access
|
||||||
|
- Pages viewed and actions taken on the Forum
|
||||||
|
- Resources accessed
|
||||||
|
- Hardware & Account identifiers
|
||||||
|
|
||||||
|
This information is collected via server logs, cookies, third party services and similar technologies.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. How We Use Your Information
|
||||||
|
|
||||||
|
We use collected information to:
|
||||||
|
- Operate and maintain our Services
|
||||||
|
- Authenticate users and manage accounts
|
||||||
|
- Moderate content and enforce rules
|
||||||
|
- Prevent abuse, fraud, and malicious activity
|
||||||
|
- Improve site functionality and performance
|
||||||
|
- Communicate with users regarding moderation actions, updates, or support requests
|
||||||
|
- Maintain community safety, security and standards
|
||||||
|
|
||||||
|
We do **not** sell personal information.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Legal Bases for Processing (GDPR / UK GDPR)
|
||||||
|
|
||||||
|
Where applicable, we process personal data under the following legal bases:
|
||||||
|
- **Consent** (e.g., account registration, optional profile information)
|
||||||
|
- **Contractual necessity** (providing Services access and functionality)
|
||||||
|
- **Legitimate interests** (security, moderation, abuse prevention, site improvement & anonymised analytics, Preserving conversational data, FAQ or documentation through the means of data anonymisation)
|
||||||
|
- **Legal obligation** (compliance with applicable laws)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Cookies and Tracking Technologies
|
||||||
|
|
||||||
|
Our Services use cookies and similar technologies to:
|
||||||
|
- Maintain login sessions
|
||||||
|
- Remember user preferences
|
||||||
|
- Improve usability and performance
|
||||||
|
|
||||||
|
Cookies may be essential for Service functionality. You can control or disable cookies through your browser settings; however, doing so may limit, restrict or break certain features or functionality.
|
||||||
|
|
||||||
|
Third party providers such as Cloudflare may employ cookies for the providing of their services. Please see their policy for how they process your data.
|
||||||
|
|
||||||
|
Where required by law and appropriate, cookie consent mechanisms are employed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Data Retention
|
||||||
|
|
||||||
|
We retain personal data only as long as necessary for the purposes described in this policy:
|
||||||
|
|
||||||
|
- **Server logs and IP addresses (non-account related):** typically retained for 90 days and a maximum of 180 days
|
||||||
|
- **Account-related IP addresses and logs relevant to moderation purposes:** retained up to 5 years
|
||||||
|
- **Account information, moderation actions and posted content:** retained until the account is deleted or content is removed, unless retention is required for moderation, security, continuity, or legal purposes
|
||||||
|
- **Right to Erasure & Data Deletion Requests:** You may request the deletion of your data through your profile within our Services. Accounts with posted content and/or moderator actions require a specific request to `dpo@beammp.com` for review on a case by case basis. Requests will be processed within 28 days in accordance with legal requirements.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Third-Party Services
|
||||||
|
|
||||||
|
We may use trusted third-party service providers to operate our Services, such as:
|
||||||
|
- Hosting providers
|
||||||
|
- Content delivery networks
|
||||||
|
- Email and notification services
|
||||||
|
- Security and anti-abuse services
|
||||||
|
- Payment processing & financial services
|
||||||
|
|
||||||
|
These providers are given access only to the information necessary to perform their services and are required by law to protect it accordingly.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Data Sharing
|
||||||
|
|
||||||
|
In order for certain functionality, the sharing of details such as account identifiers may be required. For example in providing a unique identifier to servers that you join. This may also include third party services that you choose to link with your BeamMP account.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. International Data Transfers
|
||||||
|
|
||||||
|
Data relating to our Services may be stored or processed on servers located outside your country of residence. Where required, appropriate safeguards are used to protect personal data in accordance with applicable laws (e.g., standard contractual clauses).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Your Privacy Rights
|
||||||
|
|
||||||
|
Depending on your location, you may have the right to:
|
||||||
|
- Access your personal data
|
||||||
|
- Correct inaccurate information
|
||||||
|
- Request deletion of your data
|
||||||
|
- Object to or restrict certain processing
|
||||||
|
- Request data portability
|
||||||
|
- Withdraw consent where processing is based on consent
|
||||||
|
|
||||||
|
To exercise these rights, please contact us using the information below. We may need to verify your identity before fulfilling a request.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Children’s Privacy
|
||||||
|
|
||||||
|
The Services are intended for users aged **13 or older**. We do not knowingly collect personal information from children under 13. If you believe a child has provided personal data, please contact us so it can be removed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Security
|
||||||
|
|
||||||
|
We take extensive technical and operational precautions to protect your data from accidental manipulation, loss, destruction or unauthorised access. Our security practices and mechanisms are reviewed on a regular basis and updated in accordance with best practices and industry standards.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. Changes to This Policy
|
||||||
|
|
||||||
|
We may update this Privacy Policy from time to time to reflect changes in law, technology, or Service operations. Updates will be posted on this page with a revised “Last updated” date and also communicated via our Forum at least 28 days prior to new changes taking effect.
|
||||||
|
|
||||||
|
Continued use of our Services after changes are posted constitutes acceptance of the updated policy.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. Right to complain
|
||||||
|
|
||||||
|
If you have concerns about how we handle your personal data, please contact us first so we can try to resolve the issue. You also have the right to lodge a complaint with the Information Commissioner's Office (ICO), the UK supervisory authority for data protection matters.
|
||||||
|
|
||||||
|
You can contact the ICO via the ICO website or by calling their helpline.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 14. Contact Us
|
||||||
|
|
||||||
|
If you have questions about this Privacy Policy or wish to exercise your privacy rights, please contact the BeamMP team via email at `dpo@beammp.com`.
|
||||||
@@ -0,0 +1,228 @@
|
|||||||
|
# Terms of Service (Websites, Forum, Services)
|
||||||
|
**Version 2026.6 – United Kingdom**
|
||||||
|
|
||||||
|
**Effective date:** 18th June 2026
|
||||||
|
|
||||||
|
These Terms of Service (“**Terms**”) govern your access to and use of our websites, community forum, and related online services (collectively, the “**Services**”).
|
||||||
|
This includes our Discord Server and the provided "Official Servers".
|
||||||
|
|
||||||
|
By accessing or using the Services, you agree to these Terms.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Who We Are
|
||||||
|
“**We**”, “**us**”, or “**our**” refers to the operator of the Services.
|
||||||
|
|
||||||
|
**Contact:**
|
||||||
|
Legal Name: `BeamMP Ltd.`
|
||||||
|
Email: `contact@beammp.com`
|
||||||
|
Address: `2 Queens Road, Boston, PE21 9AA, United Kingdom`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Eligibility and Age Requirements
|
||||||
|
1. **Minimum age: 13.** You must be at least **13 years old** to use the Services.
|
||||||
|
2. Users under 18 confirm they have permission from a parent or legal guardian where required by law.
|
||||||
|
3. We may apply age-appropriate safeguards, restrictions, or verification measures to comply with child-safety obligations under UK or other jurisdictional laws.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Changes to These Terms
|
||||||
|
We may update these Terms from time to time. Updated versions will be posted on the Website and, where appropriate, notified to registered users. Continued use of the Services constitutes acceptance of the updated Terms.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Accounts and Security
|
||||||
|
1. Some features require an account.
|
||||||
|
2. You must provide accurate, current information and keep it up to date as required by law.
|
||||||
|
3. You are responsible for maintaining the security of your account and all activity under it.
|
||||||
|
4. We may suspend or terminate accounts under Section 11.
|
||||||
|
5. Certain Services may use authentication keys, server keys, or access tokens (“**Auth Keys**”).
|
||||||
|
6. Auth Keys are confidential credentials and must not be shared, sold, transferred, or exposed.
|
||||||
|
7. You are responsible for all activity conducted using your Auth Keys.
|
||||||
|
8. We may revoke or rotate Auth Keys immediately if we reasonably believe they have been misused, compromised, or are being used in violation of these Terms.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Licence to Use the Services
|
||||||
|
We grant you a limited, personal, non-exclusive, non-transferable, revocable licence to access and use the Services for lawful, non-commercial purposes, subject to these Terms.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5a. User-Hosted Servers
|
||||||
|
1. The Services may allow users to host their own BeamMP servers using provided software, tools, or authentication mechanisms (“**User-Hosted Servers**”).
|
||||||
|
2. User-Hosted Servers are operated independently by users and are **not owned, controlled, or managed by us**, even where they appear in public listings.
|
||||||
|
3. We do not review, moderate, or guarantee the safety, legality, or integrity of User-Hosted Servers or their content.
|
||||||
|
4. Server operators are solely responsible for:
|
||||||
|
- Content hosted or distributed on their servers
|
||||||
|
- Mods, scripts, maps, files, or data made available
|
||||||
|
- Compliance with applicable laws and regulations
|
||||||
|
- Moderation, security, and user safety
|
||||||
|
5. You agree to indemnify us against any liability arising from your use of user-hosted servers.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Acceptable Use
|
||||||
|
You must **not** use the Services to:
|
||||||
|
|
||||||
|
1. Break the law or assist others in doing so.
|
||||||
|
2. Harass, threaten, bully, stalk, or abuse others.
|
||||||
|
3. Post or distribute illegal, hateful, sexual, or exploitative content.
|
||||||
|
4. Share personal or private information about others without consent.
|
||||||
|
5. Upload or share malware, spyware, or malicious code.
|
||||||
|
6. Attempt unauthorised access, bypass security, or disrupt the Services.
|
||||||
|
7. Impersonate others or misrepresent affiliation.
|
||||||
|
8. Evade moderation or bans using alternate accounts. You may not utilize additional or alternative accounts to circumvent moderation action by BeamMP or User-Created servers:
|
||||||
|
- using any existing account to rejoin a server you have been banned from.
|
||||||
|
- using any account, existing or newly created, after receiving a platform-level ban on any of your accounts.
|
||||||
|
|
||||||
|
We reserve the right to remove all accounts associated with a banned user upon discovery of evasion
|
||||||
|
9. Send spam or unsolicited advertising.
|
||||||
|
10. Upload, distribute, or host malicious files, exploits, backdoors, or harmful modifications on User-Hosted Servers in a manner that impacts the Services, other users, or network integrity.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Content Standards
|
||||||
|
When submitting content (“**User Content**”), you confirm that:
|
||||||
|
|
||||||
|
1. You have the legal right to submit it.
|
||||||
|
2. It does not infringe copyright, privacy, or other rights.
|
||||||
|
3. It is not unlawful, misleading, defamatory, or deceptive.
|
||||||
|
4. It does not promote violence, self-harm, illegal activity, or otherwise breach any terms outlined.
|
||||||
|
5. It does **not** include sexual content, content harmful to children or other users.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Your Content and Our Licence
|
||||||
|
1. You retain ownership of your User Content.
|
||||||
|
2. You grant us a worldwide, non-exclusive, royalty-free licence to host, store, reproduce, modify (for moderation or formatting), display, and distribute your content **solely for operating and improving the Services**.
|
||||||
|
3. Deleted content may persist in backups for a limited time.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 8a. Content on User-Hosted Servers
|
||||||
|
1. We do not host, control, or take responsibility for content uploaded to User-Hosted Servers.
|
||||||
|
2. This includes, but is not limited to:
|
||||||
|
- Mods, scripts, plugins, maps, assets, or configuration files
|
||||||
|
- Executable or downloadable files
|
||||||
|
- Third-party or user-created content
|
||||||
|
3. Any content uploaded to or distributed through User-Hosted Servers is done at the server operator’s and user’s own risk.
|
||||||
|
4. We are not responsible for damage, data loss, security incidents, or harm arising from content hosted on User-Hosted Servers.
|
||||||
|
5. We may take action against accounts or servers that use User-Hosted Servers to distribute malware, illegal content, or content that threatens the integrity of the Services or its users.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Intellectual Property
|
||||||
|
All intellectual property rights in the Services (including software, branding, and design) belong to us or our licensors. You may not copy, modify, sell, or reverse-engineer any part of the Services except as permitted by law.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Copyright and Takedowns
|
||||||
|
If you believe content infringes your copyright or other intellectual property rights, contact us with:
|
||||||
|
|
||||||
|
- Identification of the content or works
|
||||||
|
- Location of the infringing material
|
||||||
|
- Your contact details
|
||||||
|
- A good-faith statement of accuracy
|
||||||
|
- Proof of ownership, rights, authority or authenticity
|
||||||
|
|
||||||
|
We may remove infringing material and take action at our discretion.
|
||||||
|
|
||||||
|
If your content is removed and you believe this was an error, you may submit a counter-notice to contact@beammp.com including the removed content's details, your contact information, and a good-faith explanation.
|
||||||
|
We will review and may reinstate where appropriate.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Enforcement and Moderation
|
||||||
|
1. We may remove content, restrict features, suspend, or terminate accounts for violation of our Terms, Rules or Policies.
|
||||||
|
2. We cooperate with law enforcement where legally required, and may share information voluntarily where we reasonably consider it necessary to protect the safety of users or the public, or to prevent or detect crime, in line with our Privacy Policy and applicable data-protection law.
|
||||||
|
3. We make a reasonable effort to proactively monitor content on our Services using a combination of community reports, automated tools and active moderation.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. Reporting and Complaints
|
||||||
|
We provide mechanisms to report:
|
||||||
|
|
||||||
|
- Illegal or harmful content
|
||||||
|
- Harassment or abuse
|
||||||
|
- Child-safety concerns
|
||||||
|
- Privacy or data-protection issues
|
||||||
|
- Moderation appeals
|
||||||
|
- Breach of Rules or Terms of Service
|
||||||
|
- General concerns or queries
|
||||||
|
|
||||||
|
Reports will be handled proportionately and within reasonable timeframes, on a case by case basis.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. UK Online Safety Act Compliance
|
||||||
|
Where applicable, the Services are operated in line with the **UK Online Safety Act**, including:
|
||||||
|
|
||||||
|
1. Measures to reduce the risk of illegal content.
|
||||||
|
2. Systems to protect children from harmful material.
|
||||||
|
3. Accessible reporting and appeals processes.
|
||||||
|
4. Proportionate enforcement of these Terms.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 14. Privacy and Data Protection
|
||||||
|
1. Personal data is processed in accordance with **GDPR**.
|
||||||
|
2. Our **Privacy Policy** explains what data we collect, how it is used, and your rights.
|
||||||
|
3. Lawful bases for processing may include contract performance, legitimate interests (security and moderation), and legal obligations.
|
||||||
|
4. You may request access, correction, deletion, or restriction of your personal data.
|
||||||
|
5. Certain data may be retained where necessary for security, legal compliance, or enforcement.
|
||||||
|
|
||||||
|
Requests in relation to the above or any other query or concern can be raised via email with `dpo@beammp.com`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 15. Third-Party Services
|
||||||
|
Our Services may link to or integrate with third-party services. We are not responsible for third-party content, services, or policies.
|
||||||
|
Please see their respective policies for further information on how they operate.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 15a. Discord and External Platforms
|
||||||
|
1. The Services may include official or community-operated spaces on third-party platforms, including Discord.
|
||||||
|
2. Use of such platforms is subject to their own terms, rules, and policies.
|
||||||
|
3. We are not responsible for outages, moderation actions, or data handling by third-party platforms.
|
||||||
|
4. Violations of our rules on third-party platforms may be considered when enforcing these Terms where relevant to community safety.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 16. Disclaimers
|
||||||
|
The Services are provided **“as is”** and **“as available.”** To the maximum extent permitted by law, we disclaim all warranties, including fitness for purpose and non-infringement.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 17. Limitation of Liability
|
||||||
|
To the extent permitted by law:
|
||||||
|
|
||||||
|
1. We are not liable for indirect, incidental, or consequential damages.
|
||||||
|
2. Our total liability is limited to the greater of:
|
||||||
|
- The amount you paid us in the past 12 months (if any), or
|
||||||
|
- £100
|
||||||
|
3. Nothing limits liability where it cannot be excluded under UK law.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 18. Indemnity
|
||||||
|
You agree to indemnify us against losses arising from your breach of these Terms or unlawful use of the Services.
|
||||||
|
This includes claims arising from content, files, or services operated through User-Hosted Servers.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 19. Termination
|
||||||
|
You may stop using the Services at any time. We may suspend or terminate access for violations, legal obligations, or security reasons.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 20. Governing Law and Jurisdiction
|
||||||
|
These Terms are governed by the laws of **England and Wales**, and disputes are subject to the exclusive jurisdiction of its courts.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 21. Miscellaneous
|
||||||
|
1. If any provision is unenforceable, the remainder remains effective.
|
||||||
|
2. You may not transfer your rights under these Terms without consent.
|
||||||
|
3. These Terms and the Privacy Policy constitute the entire agreement regarding the Services.
|
||||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 365 KiB |
+10
-16
@@ -1,10 +1,4 @@
|
|||||||
[
|
[
|
||||||
{
|
|
||||||
"name": "Horizon Hosting",
|
|
||||||
"website": "https://hrzn.link/beammp",
|
|
||||||
"from": "$3.34/mo",
|
|
||||||
"logo": "https://hrznhosting.com/assets/logo.svg"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "RackGenius",
|
"name": "RackGenius",
|
||||||
"website": "https://rackgeni.us/beammp-plans",
|
"website": "https://rackgeni.us/beammp-plans",
|
||||||
@@ -15,7 +9,7 @@
|
|||||||
"name": "Connect Hosting",
|
"name": "Connect Hosting",
|
||||||
"website": "https://connecthosting.net/beammp",
|
"website": "https://connecthosting.net/beammp",
|
||||||
"from": "$1.49/mo",
|
"from": "$1.49/mo",
|
||||||
"logo": "https://connecthosting.net/img/logo.webp"
|
"logo": "https://connecthosting.net/logo.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Assetto Hosting",
|
"name": "Assetto Hosting",
|
||||||
@@ -31,7 +25,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "HostHavoc",
|
"name": "HostHavoc",
|
||||||
"website": "https://hosthavoc.com",
|
"website": "https://hosthavoc.com/game-servers/beammp",
|
||||||
"from": "$3.75/mo",
|
"from": "$3.75/mo",
|
||||||
"logo": "https://hosthavoc.com/images/logo.svg"
|
"logo": "https://hosthavoc.com/images/logo.svg"
|
||||||
},
|
},
|
||||||
@@ -48,23 +42,17 @@
|
|||||||
"logo": "https://vyperhosting.com/assets/logo.png"
|
"logo": "https://vyperhosting.com/assets/logo.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "BisecHosting",
|
"name": "BisectHosting",
|
||||||
"website": "https://bisecthosting.com/beammp-server-hosting",
|
"website": "https://bisecthosting.com/beammp-server-hosting",
|
||||||
"from": "$5.99/mo",
|
"from": "$5.99/mo",
|
||||||
"logo": "https://www.bisecthosting.com/_ipx/q_100&s_140x46/images/logo-dark-theme.svg"
|
"logo": "https://www.bisecthosting.com/_ipx/q_100&s_140x46/images/logo-dark-theme.svg"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Four Season Hosting",
|
"name": "Four Seasons Hosting",
|
||||||
"website": "https://fourseasonshosting.com",
|
"website": "https://fourseasonshosting.com",
|
||||||
"from": "$3.00/mo",
|
"from": "$3.00/mo",
|
||||||
"logo": "https://fourseasonshosting.com/_next/image?url=%2Flogo.png&w=256&q=75"
|
"logo": "https://fourseasonshosting.com/_next/image?url=%2Flogo.png&w=256&q=75"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "Vertuo Hosting",
|
|
||||||
"website": "https://vertuohosting.com",
|
|
||||||
"from": "$1.99/mo",
|
|
||||||
"logo": "https://vertuohosting.com/assets/img/logo.png"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "Winheberg",
|
"name": "Winheberg",
|
||||||
"website": "https://winheberg.fr/offres/gaming/beammp",
|
"website": "https://winheberg.fr/offres/gaming/beammp",
|
||||||
@@ -82,5 +70,11 @@
|
|||||||
"website": "https://iceline-hosting.com/games/beammp",
|
"website": "https://iceline-hosting.com/games/beammp",
|
||||||
"from": "$3.64/mo",
|
"from": "$3.64/mo",
|
||||||
"logo": "https://iceline-hosting.com/_astro/logo.DTnWV5zL_157TDr.webp"
|
"logo": "https://iceline-hosting.com/_astro/logo.DTnWV5zL_157TDr.webp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "TLS Servers-Studios",
|
||||||
|
"website": "https://tlsservers.com/beammp",
|
||||||
|
"from": "$2.00/mo",
|
||||||
|
"logo": "/partner_logos/TLS_Servers-Studios.png"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 157 KiB After Width: | Height: | Size: 157 KiB |
|
Before Width: | Height: | Size: 16 MiB After Width: | Height: | Size: 16 MiB |
@@ -198,7 +198,7 @@ const TikTokIcon = createLucideIcon('TikTok', [
|
|||||||
</RouterLink>
|
</RouterLink>
|
||||||
<span>·</span>
|
<span>·</span>
|
||||||
<a
|
<a
|
||||||
href="https://forum.beammp.com/topic/95/privacy-policy-v1-0"
|
href="https://beammp.com/privacy"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
class="text-neutral-700 hover:text-beammp-blue transition-colors dark:text-neutral-500 dark:hover:text-blue-400"
|
class="text-neutral-700 hover:text-beammp-blue transition-colors dark:text-neutral-500 dark:hover:text-blue-400"
|
||||||
@@ -207,7 +207,7 @@ const TikTokIcon = createLucideIcon('TikTok', [
|
|||||||
</a>
|
</a>
|
||||||
<span>·</span>
|
<span>·</span>
|
||||||
<a
|
<a
|
||||||
href="https://forum.beammp.com/topic/94/terms-of-use-v1-0"
|
href="https://beammp.com/terms"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
class="text-neutral-700 hover:text-beammp-blue transition-colors dark:text-neutral-500 dark:hover:text-blue-400"
|
class="text-neutral-700 hover:text-beammp-blue transition-colors dark:text-neutral-500 dark:hover:text-blue-400"
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ function localRoute(path) {
|
|||||||
<div class="hidden xl:flex items-center gap-4">
|
<div class="hidden xl:flex items-center gap-4">
|
||||||
<NavigationMenu>
|
<NavigationMenu>
|
||||||
<NavigationMenuList>
|
<NavigationMenuList>
|
||||||
<NavigationMenuItem>
|
<!--<NavigationMenuItem>
|
||||||
<NavigationMenuLink as-child>
|
<NavigationMenuLink as-child>
|
||||||
<RouterLink
|
<RouterLink
|
||||||
:to="localRoute('communities')"
|
:to="localRoute('communities')"
|
||||||
@@ -74,7 +74,7 @@ function localRoute(path) {
|
|||||||
{{ $t('message.nav.communities') }}
|
{{ $t('message.nav.communities') }}
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
</NavigationMenuLink>
|
</NavigationMenuLink>
|
||||||
</NavigationMenuItem>
|
</NavigationMenuItem>-->
|
||||||
<NavigationMenuItem>
|
<NavigationMenuItem>
|
||||||
<NavigationMenuLink as-child>
|
<NavigationMenuLink as-child>
|
||||||
<RouterLink
|
<RouterLink
|
||||||
@@ -105,7 +105,7 @@ function localRoute(path) {
|
|||||||
</RouterLink>
|
</RouterLink>
|
||||||
</NavigationMenuLink>
|
</NavigationMenuLink>
|
||||||
</NavigationMenuItem>
|
</NavigationMenuItem>
|
||||||
<NavigationMenuItem>
|
<!--<NavigationMenuItem>
|
||||||
<NavigationMenuLink as-child>
|
<NavigationMenuLink as-child>
|
||||||
<RouterLink
|
<RouterLink
|
||||||
:to="localRoute('stats')"
|
:to="localRoute('stats')"
|
||||||
@@ -119,8 +119,8 @@ function localRoute(path) {
|
|||||||
{{ $t('message.nav.statistics') }}
|
{{ $t('message.nav.statistics') }}
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
</NavigationMenuLink>
|
</NavigationMenuLink>
|
||||||
</NavigationMenuItem>
|
</NavigationMenuItem>-->
|
||||||
<NavigationMenuItem>
|
<!--<NavigationMenuItem>
|
||||||
<NavigationMenuLink as-child>
|
<NavigationMenuLink as-child>
|
||||||
<a
|
<a
|
||||||
href="https://forum.beammp.com"
|
href="https://forum.beammp.com"
|
||||||
@@ -137,7 +137,7 @@ function localRoute(path) {
|
|||||||
{{ $t('message.nav.forums') }}
|
{{ $t('message.nav.forums') }}
|
||||||
</a>
|
</a>
|
||||||
</NavigationMenuLink>
|
</NavigationMenuLink>
|
||||||
</NavigationMenuItem>
|
</NavigationMenuItem>-->
|
||||||
<NavigationMenuItem>
|
<NavigationMenuItem>
|
||||||
<NavigationMenuLink as-child>
|
<NavigationMenuLink as-child>
|
||||||
<a
|
<a
|
||||||
@@ -247,13 +247,13 @@ function localRoute(path) {
|
|||||||
class="xl:hidden border-t border-neutral-200 dark:border-neutral-800 bg-white dark:bg-neutral-900"
|
class="xl:hidden border-t border-neutral-200 dark:border-neutral-800 bg-white dark:bg-neutral-900"
|
||||||
>
|
>
|
||||||
<div class="px-4 py-3 space-y-1">
|
<div class="px-4 py-3 space-y-1">
|
||||||
<RouterLink
|
<!--<RouterLink
|
||||||
:to="localRoute('communities')"
|
:to="localRoute('communities')"
|
||||||
class="block px-4 py-3 text-neutral-900 dark:text-white hover:bg-neutral-100 dark:hover:bg-neutral-800 rounded-md transition-colors"
|
class="block px-4 py-3 text-neutral-900 dark:text-white hover:bg-neutral-100 dark:hover:bg-neutral-800 rounded-md transition-colors"
|
||||||
@click="closeMobileMenu"
|
@click="closeMobileMenu"
|
||||||
>
|
>
|
||||||
{{ $t('message.nav.communities') }}
|
{{ $t('message.nav.communities') }}
|
||||||
</RouterLink>
|
</RouterLink>-->
|
||||||
<RouterLink
|
<RouterLink
|
||||||
:to="localRoute('partners')"
|
:to="localRoute('partners')"
|
||||||
class="block px-4 py-3 text-neutral-900 dark:text-white hover:bg-neutral-100 dark:hover:bg-neutral-800 rounded-md transition-colors"
|
class="block px-4 py-3 text-neutral-900 dark:text-white hover:bg-neutral-100 dark:hover:bg-neutral-800 rounded-md transition-colors"
|
||||||
@@ -268,14 +268,14 @@ function localRoute(path) {
|
|||||||
>
|
>
|
||||||
{{ $t('message.nav.servers') }}
|
{{ $t('message.nav.servers') }}
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
<RouterLink
|
<!--<RouterLink
|
||||||
:to="localRoute('stats')"
|
:to="localRoute('stats')"
|
||||||
class="block px-4 py-3 text-neutral-900 dark:text-white hover:bg-neutral-100 dark:hover:bg-neutral-800 rounded-md transition-colors"
|
class="block px-4 py-3 text-neutral-900 dark:text-white hover:bg-neutral-100 dark:hover:bg-neutral-800 rounded-md transition-colors"
|
||||||
@click="closeMobileMenu"
|
@click="closeMobileMenu"
|
||||||
>
|
>
|
||||||
{{ $t('message.nav.statistics') }}
|
{{ $t('message.nav.statistics') }}
|
||||||
</RouterLink>
|
</RouterLink>-->
|
||||||
<a
|
<!--<a
|
||||||
href="https://forum.beammp.com"
|
href="https://forum.beammp.com"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
@@ -283,7 +283,7 @@ function localRoute(path) {
|
|||||||
@click="closeMobileMenu"
|
@click="closeMobileMenu"
|
||||||
>
|
>
|
||||||
{{ $t('message.nav.forums') }}
|
{{ $t('message.nav.forums') }}
|
||||||
</a>
|
</a>-->
|
||||||
<a
|
<a
|
||||||
href="https://docs.beammp.com"
|
href="https://docs.beammp.com"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
|||||||
@@ -37,6 +37,11 @@ const toggleDropdown = () => {
|
|||||||
isOpen.value = !isOpen.value
|
isOpen.value = !isOpen.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getFlagUrl = (flagName) => {
|
||||||
|
//return new URL(`../assets/flags/${flagName}.png`, import.meta.url).href
|
||||||
|
return `/flags/${flagName}.png`
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
const saved = localStorage.getItem('lang') || getCurrentLocale()
|
const saved = localStorage.getItem('lang') || getCurrentLocale()
|
||||||
if (saved && saved !== locale.value) {
|
if (saved && saved !== locale.value) {
|
||||||
@@ -60,7 +65,7 @@ onMounted(async () => {
|
|||||||
@click="toggleDropdown"
|
@click="toggleDropdown"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
:src="`/flags/${currentLanguage().flag}.png`"
|
:src="getFlagUrl(currentLanguage().flag)"
|
||||||
:alt="currentLanguage().name"
|
:alt="currentLanguage().name"
|
||||||
class="w-5 h-4 rounded-sm"
|
class="w-5 h-4 rounded-sm"
|
||||||
/>
|
/>
|
||||||
@@ -85,7 +90,7 @@ onMounted(async () => {
|
|||||||
]"
|
]"
|
||||||
@click="selectLanguage(code)"
|
@click="selectLanguage(code)"
|
||||||
>
|
>
|
||||||
<img :src="`/flags/${lang.flag}.png`" :alt="lang.name" class="w-5 h-4 rounded-sm" />
|
<img :src="getFlagUrl(lang.flag)" :alt="lang.name" class="w-5 h-4 rounded-sm" />
|
||||||
<span>{{ lang.name }}</span>
|
<span>{{ lang.name }}</span>
|
||||||
<span v-if="locale === code" class="ml-auto text-neutral-500">✓</span>
|
<span v-if="locale === code" class="ml-auto text-neutral-500">✓</span>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { reactiveOmit } from "@vueuse/core";
|
import { reactiveOmit } from '@vueuse/core'
|
||||||
import { Check } from "lucide-vue-next";
|
import { Check } from 'lucide-vue-next'
|
||||||
import { CheckboxIndicator, CheckboxRoot, useForwardPropsEmits } from "reka-ui";
|
import { CheckboxIndicator, CheckboxRoot, useForwardPropsEmits } from 'reka-ui'
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
defaultValue: { type: [Boolean, String], required: false },
|
defaultValue: { type: [Boolean, String], required: false },
|
||||||
@@ -15,12 +15,12 @@ const props = defineProps({
|
|||||||
name: { type: String, required: false },
|
name: { type: String, required: false },
|
||||||
required: { type: Boolean, required: false },
|
required: { type: Boolean, required: false },
|
||||||
class: { type: null, required: false },
|
class: { type: null, required: false },
|
||||||
});
|
})
|
||||||
const emits = defineEmits(["update:modelValue"]);
|
const emits = defineEmits(['update:modelValue'])
|
||||||
|
|
||||||
const delegatedProps = reactiveOmit(props, "class");
|
const delegatedProps = reactiveOmit(props, 'class')
|
||||||
|
|
||||||
const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -29,7 +29,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
|||||||
:class="
|
:class="
|
||||||
cn(
|
cn(
|
||||||
'grid place-content-center peer h-4 w-4 shrink-0 rounded-sm border border-primary shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground',
|
'grid place-content-center peer h-4 w-4 shrink-0 rounded-sm border border-primary shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground',
|
||||||
props.class,
|
props.class
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
export { default as Checkbox } from "./Checkbox.vue";
|
export { default as Checkbox } from './Checkbox.vue'
|
||||||
|
|||||||
+2
-1
@@ -1,7 +1,7 @@
|
|||||||
import { nextTick } from 'vue'
|
import { nextTick } from 'vue'
|
||||||
import { createI18n } from 'vue-i18n'
|
import { createI18n } from 'vue-i18n'
|
||||||
|
|
||||||
export const SUPPORT_LOCALES = ['en', 'es', 'fr', 'de', 'it', 'ru']
|
export const SUPPORT_LOCALES = ['en', 'es', 'fr', 'de', 'it', 'ru', 'zh']
|
||||||
|
|
||||||
export const LANGUAGES = {
|
export const LANGUAGES = {
|
||||||
en: { name: 'English', code: 'en', flag: 'gb' },
|
en: { name: 'English', code: 'en', flag: 'gb' },
|
||||||
@@ -10,6 +10,7 @@ export const LANGUAGES = {
|
|||||||
de: { name: 'Deutsch', code: 'de', flag: 'de' },
|
de: { name: 'Deutsch', code: 'de', flag: 'de' },
|
||||||
it: { name: 'Italiano', code: 'it', flag: 'it' },
|
it: { name: 'Italiano', code: 'it', flag: 'it' },
|
||||||
ru: { name: 'Русский', code: 'ru', flag: 'ru' },
|
ru: { name: 'Русский', code: 'ru', flag: 'ru' },
|
||||||
|
zh: { name: '中文', code: 'zh', flag: 'cn' },
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setupI18n(options = { locale: 'en' }) {
|
export function setupI18n(options = { locale: 'en' }) {
|
||||||
|
|||||||
+6
-12
@@ -133,33 +133,27 @@
|
|||||||
"title": "Frequently Asked Questions",
|
"title": "Frequently Asked Questions",
|
||||||
"0": {
|
"0": {
|
||||||
"question": "The server list is not showing up!",
|
"question": "The server list is not showing up!",
|
||||||
"answer":
|
"answer": "Try restarting BeamMP as this can sometimes happen. If this fails to fix your issue please create a topic on our forum or visit the support channel on our discord."
|
||||||
"Try restarting BeamMP as this can sometimes happen. If this fails to fix your issue please create a topic on our forum or visit the support channel on our discord."
|
|
||||||
},
|
},
|
||||||
"1": {
|
"1": {
|
||||||
"question": "How do I open a ticket in case something doesn't work or I have questions?",
|
"question": "How do I open a ticket in case something doesn't work or I have questions?",
|
||||||
"answer":
|
"answer": "Please check the #how-to-use channel in Discord and on our forum. Please give an accurate description of what you have done so the support team will help you in a fast and effective way."
|
||||||
"Please check the #how-to-use channel in Discord and on our forum. Please give an accurate description of what you have done so the support team will help you in a fast and effective way."
|
|
||||||
},
|
},
|
||||||
"2": {
|
"2": {
|
||||||
"question": "Help! I'm getting error codes",
|
"question": "Help! I'm getting error codes",
|
||||||
"answer":
|
"answer": "Please head over to our forum and see if anyone has had the issue before. It is likely that someone else has at some point and therefore there will also probably be a solution posted with it too in the replies. Alternatively visit our discord where the larger portion of community is currently based."
|
||||||
"Please head over to our forum and see if anyone has had the issue before. It is likely that someone else has at some point and therefore there will also probably be a solution posted with it too in the replies. Alternatively visit our discord where the larger portion of community is currently based."
|
|
||||||
},
|
},
|
||||||
"3": {
|
"3": {
|
||||||
"question": "Does this work with pirated versions of BeamNG.drive?",
|
"question": "Does this work with pirated versions of BeamNG.drive?",
|
||||||
"answer":
|
"answer": "We don't know if it works with pirated versions of BeamNG.drive, but we will not provide any support to non legit copies of the game. Nor will we change to support it."
|
||||||
"We don't know if it works with pirated versions of BeamNG.drive, but we will not provide any support to non legit copies of the game. Nor will we change to support it."
|
|
||||||
},
|
},
|
||||||
"4": {
|
"4": {
|
||||||
"question": "How do I host a server?",
|
"question": "How do I host a server?",
|
||||||
"answer":
|
"answer": "The server files required for hosting your own server can be found at the top of this page below the client download. You will also require an authentication key which can be found from keymaster. Further information around the setup can be found on our docs."
|
||||||
"The server files required for hosting your own server can be found at the top of this page below the client download. You will also require an authentication key which can be found from keymaster. Further information around the setup can be found on our docs."
|
|
||||||
},
|
},
|
||||||
"5": {
|
"5": {
|
||||||
"question": "Can I use mods?",
|
"question": "Can I use mods?",
|
||||||
"answer":
|
"answer": "Mods are supported. These are installed on the server. See our docs for more information."
|
||||||
"Mods are supported. These are installed on the server. See our docs for more information."
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"stats": {
|
"stats": {
|
||||||
|
|||||||
@@ -0,0 +1,278 @@
|
|||||||
|
{
|
||||||
|
"message": {
|
||||||
|
"theme": {
|
||||||
|
"light": "明亮模式",
|
||||||
|
"dark": "黑暗模式",
|
||||||
|
"system": "跟随系统"
|
||||||
|
},
|
||||||
|
"nav": {
|
||||||
|
"home": "主页",
|
||||||
|
"forums": "论坛",
|
||||||
|
"docs": "文档",
|
||||||
|
"communities": "社区",
|
||||||
|
"partners": "托管合作伙伴",
|
||||||
|
"servers": "服务器",
|
||||||
|
"statistics": "统计",
|
||||||
|
"store": "周边商店",
|
||||||
|
"github": "GitHub",
|
||||||
|
"patreon": "在 Patreon 上为我们捐献",
|
||||||
|
"language": "选择语言",
|
||||||
|
"theme": {
|
||||||
|
"light": "明亮主题",
|
||||||
|
"dark": "黑暗主题",
|
||||||
|
"system": "跟随系统"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"footer": {
|
||||||
|
"support_on_patreon": "Patreon",
|
||||||
|
"about": "关于我们",
|
||||||
|
"privacy_policy": "隐私政策",
|
||||||
|
"terms_conditions": "条款与细则"
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"title": "找不到页面",
|
||||||
|
"description": "此页面不存在,\n此页面可能不存在或已经被删除",
|
||||||
|
"return_home": "返回首页"
|
||||||
|
},
|
||||||
|
"home": {
|
||||||
|
"hero": {
|
||||||
|
"title": "为BeamNG.Drive 提供多人游戏",
|
||||||
|
"subtitle": "联机体验软体物理引擎\n和朋友一起赛车 角色扮演或闲逛",
|
||||||
|
"download_now": "现在下载",
|
||||||
|
"browse_servers": "查看服务器"
|
||||||
|
},
|
||||||
|
"metrics": {
|
||||||
|
"active_players": "活跃玩家",
|
||||||
|
"players_online": "在线玩家",
|
||||||
|
"public_servers": "公开服务器",
|
||||||
|
"all_servers": "所有服务器"
|
||||||
|
},
|
||||||
|
"why_beammp": "为什么选择BeamMP?",
|
||||||
|
"features": {
|
||||||
|
"stable_servers": {
|
||||||
|
"title": "稳定",
|
||||||
|
"description": "BeamMP在全球开设服务器 提供稳定体验"
|
||||||
|
},
|
||||||
|
"beamng": {
|
||||||
|
"title": "易用",
|
||||||
|
"description": "BeamMP与BeamNG使用相同的地图 车辆和模组 无需学习新东西"
|
||||||
|
},
|
||||||
|
"standalone": {
|
||||||
|
"title": "独立",
|
||||||
|
"description": "BeamMP不会修改你的BeamNG 你可以无缝使用单机和联机"
|
||||||
|
},
|
||||||
|
"sync": {
|
||||||
|
"title": "流畅",
|
||||||
|
"description": "BeamMP 每秒更新~50次同步 带来流畅的联机体验"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"communities": {
|
||||||
|
"join": "加入这个充满活力的社区",
|
||||||
|
"description": "探索数千个独特服务器上的多样化游戏体验",
|
||||||
|
"racing": {
|
||||||
|
"name": "赛车",
|
||||||
|
"description": "为全球玩家提供联机赛车与计时赛"
|
||||||
|
},
|
||||||
|
"roleplay": {
|
||||||
|
"name": "角色扮演",
|
||||||
|
"description": "从警匪追逐到货运模拟都能获得沉浸的体验"
|
||||||
|
},
|
||||||
|
"crash": {
|
||||||
|
"name": "破坏",
|
||||||
|
"description": "以德比和破坏为玩法的主题玩法"
|
||||||
|
},
|
||||||
|
"freeroam": {
|
||||||
|
"name": "自由漫游与自定义服务器",
|
||||||
|
"description": "探索开放世界服务器,或使用自定义脚本、地图和模组构建你自己的服务器。"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"find": {
|
||||||
|
"title": "寻找你的完美服务器",
|
||||||
|
"description": "查看数百个活跃服务器 拥有不同的游戏模式 模组和社区 从竞速到休闲 每个人都能在这里找到适合自己的服务器",
|
||||||
|
"points": {
|
||||||
|
"custom": "独特体验",
|
||||||
|
"custom_desc": "每个玩法都由社区创造",
|
||||||
|
"moderation": "环境友好",
|
||||||
|
"moderation_desc": "安全友好的游戏环境",
|
||||||
|
"global": "极低延迟",
|
||||||
|
"global_desc": "全球服务器实现低延迟"
|
||||||
|
},
|
||||||
|
"browse_all_servers": "浏览全部服务器"
|
||||||
|
},
|
||||||
|
"devFeatures": {
|
||||||
|
"title": "为开发者而生",
|
||||||
|
"description": "打造独特游戏规则 开设自己的服务器 为项目做出贡献",
|
||||||
|
"lua": {
|
||||||
|
"title": "Lua API",
|
||||||
|
"description": "强大的服务器端Lua脚本支持,用于实现自定义游戏模式与功能。"
|
||||||
|
},
|
||||||
|
"docs": {
|
||||||
|
"title": "文档",
|
||||||
|
"description": "面向服务器开发的详尽指南与API参考文档"
|
||||||
|
},
|
||||||
|
"openSource": {
|
||||||
|
"title": "开源",
|
||||||
|
"description": "采用社区驱动开发模式,并在GitHub上开源"
|
||||||
|
},
|
||||||
|
"learn_more": "了解更多",
|
||||||
|
"host": {
|
||||||
|
"title": "准备搭建您的专属服务器了吗?",
|
||||||
|
"description": "下载服务器文件,即可打造您独特的BeamMP游戏体验。",
|
||||||
|
"windows": "Windows 服务器端",
|
||||||
|
"linux": "Linux 服务器端",
|
||||||
|
"partners": {
|
||||||
|
"description": "通过我们可信赖的托管合作伙伴快速上手,享受无忧设置、24/7技术支持和优化性能。",
|
||||||
|
"view_partners": "查看托管合作伙伴"
|
||||||
|
},
|
||||||
|
"self_host": {
|
||||||
|
"title": "或者自托管"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"faq": {
|
||||||
|
"title": "常见问题回答",
|
||||||
|
"0": {
|
||||||
|
"question": "服务器列表没有显示!",
|
||||||
|
"answer": "如果遇到此问题,可尝试重启BeamMP客户端。若仍未解决,请在官方论坛发帖或加入Discord支持频道寻求帮助。"
|
||||||
|
},
|
||||||
|
"1": {
|
||||||
|
"question": "若遇到问题或有疑问,如何提交工单?",
|
||||||
|
"answer": "请查看Discord及官方论坛中的#how-to-use频道。详细描述您已尝试的操作步骤,以便支持团队能够快速有效地为您提供帮助。"
|
||||||
|
},
|
||||||
|
"2": {
|
||||||
|
"question": "求助!我遇到错误代码了",
|
||||||
|
"answer": "请前往我们的论坛查看是否有人遇到过相同问题。很可能已有其他用户遇到过类似情况,回复中通常也会附有解决方案。或者可以访问我们的Discord频道,那里是目前更活跃的社区,大家都很热心帮助。"
|
||||||
|
},
|
||||||
|
"3": {
|
||||||
|
"question": "我可以在盗版的BeamNG.Drive上玩吗?",
|
||||||
|
"answer": "我们尚不确定其是否兼容BeamNG.drive的盗版版本,但对于非正版游戏副本,我们将不提供任何技术支持,也不会兼容它"
|
||||||
|
},
|
||||||
|
"4": {
|
||||||
|
"question": "如何托管服务器?",
|
||||||
|
"answer": "托管服务器所需的文件可在本页顶部客户端下载下方找到。同时您需要在Keymaster获取认证密钥。更多设置相关信息请查阅我们的官方文档。"
|
||||||
|
},
|
||||||
|
"5": {
|
||||||
|
"question": "我可以使用模组吗?",
|
||||||
|
"answer": "模组是支持的,这些安装在服务器上。请查看我们的文档以获取更多信息。"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"stats": {
|
||||||
|
"active_players": "活跃玩家",
|
||||||
|
"global_servers": "全球服务器",
|
||||||
|
"total_downloads": "总下载量"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"communities": {
|
||||||
|
"title": "社区",
|
||||||
|
"description": "发现围绕BeamMP建立的繁荣社区。从竞技赛车联盟到休闲自由漫游会话,找到您完美的玩伴群组。",
|
||||||
|
"starting": {
|
||||||
|
"title": "想要创建您自己的社区吗?",
|
||||||
|
"description": "托管您自己的BeamMP服务器,并围绕您钟爱的游戏模式建立社区。",
|
||||||
|
"setup_guide": "服务器设置指南",
|
||||||
|
"join_discord": "加入我们的Discord"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"partners": {
|
||||||
|
"title": "托管合作伙伴",
|
||||||
|
"description": "选择我们值得信赖的托管合作伙伴。简单的设置、优化的性能和支持,以确保您的BeamMP服务器运行顺畅。",
|
||||||
|
"from_price": "从 {price}",
|
||||||
|
"visit_website": "访问网站",
|
||||||
|
"error_loading": "无法获取合作伙伴信息"
|
||||||
|
},
|
||||||
|
"servers": {
|
||||||
|
"title": "服务器",
|
||||||
|
"description": "探索并加入来自全球的BeamMP服务器。在此一站式发现您钟爱的游戏模式、活跃社群与丰富玩法。",
|
||||||
|
"show_only": "仅显示:",
|
||||||
|
"loading_servers": "加载服务器中...",
|
||||||
|
"server_count": "0 个发现的服务器 | 1 个发现的服务器 | {count} 发现的服务器",
|
||||||
|
"players_found": "0 个玩家 | 1 个玩家 | {count} 个玩家",
|
||||||
|
"filters": {
|
||||||
|
"hide_empty": "隐藏空服务器",
|
||||||
|
"hide_full": "隐藏爆满服务器",
|
||||||
|
"search": "搜索服务器中",
|
||||||
|
"hide_password": "隐藏受密码保护的服务器",
|
||||||
|
"show_official": "官方服务器",
|
||||||
|
"show_partner": "合作伙伴服务器",
|
||||||
|
"show_featured": "推荐服务器"
|
||||||
|
},
|
||||||
|
"table_headers": {
|
||||||
|
"location": "位置",
|
||||||
|
"name": "服务器名称",
|
||||||
|
"map": "地图",
|
||||||
|
"players": "玩家",
|
||||||
|
"mods": "模组",
|
||||||
|
"status": "状态"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"statistics": {
|
||||||
|
"title": "统计数据",
|
||||||
|
"description": "提供活跃服务器的实时快照指标与玩家数量时间线。支持在图表上标注版本发布日期。",
|
||||||
|
"loading": "加载中...",
|
||||||
|
"peak": "峰值: {count}",
|
||||||
|
"current_players": "当前玩家",
|
||||||
|
"current_servers": "当前服务器",
|
||||||
|
"avg_players_per_server": "服务器平均玩家数量",
|
||||||
|
"official_servers": "官方服务器",
|
||||||
|
"partner_servers": "合作伙伴服务器",
|
||||||
|
"player_volume": "在线人数时间轴",
|
||||||
|
"hint": "版本发布标记显示为橙色垂直虚线(例如 v3.0.0、v4.0.0)。悬停鼠标至数据点可查看详细信息。",
|
||||||
|
"server_volume": "服务器数量时间轴",
|
||||||
|
"server_hint": "服务器数量呈现相似趋势。发布标记指示主要版本发布。"
|
||||||
|
},
|
||||||
|
"about": {
|
||||||
|
"title": "关于BeamMP",
|
||||||
|
"description": "BeamMP可为BeamNG.drive带来多人游戏体验。它由社区共建、为社区服务,注重稳定性与性能,致力于让您与朋友共享真实的驾驶体验。",
|
||||||
|
"note": {
|
||||||
|
"title": "开发者的话",
|
||||||
|
"content": "BeamMP 始于一个简单的想法:我想和我的兄弟们一起玩BeamNG.drive。这个最初作为实验的项目,迅速成长为一个社区驱动的倡议,专注于实现多人游戏体验与乐趣。我们深切关注于提供尽可能最佳的用户体验,并让玩家能够轻松加入,享受共同驾驶的快乐。该项目向所有技能水平的贡献者开放——无论您是编写代码、管理服务器、设计场景,还是帮助他人完成设置,您的努力都是 BeamMP 蓬勃发展的关键部分。感谢您的参与,帮助我们共同构建这个非凡的项目。"
|
||||||
|
},
|
||||||
|
"provides": {
|
||||||
|
"title": "BeamMP的功能与服务",
|
||||||
|
"points": {
|
||||||
|
"0": "提供BeamNG.drive的多人联机会话,支持服务器浏览器与筛选功能。",
|
||||||
|
"1": "服务器端管理工具与配置选项",
|
||||||
|
"2": "模组支持与付费内容保护机制",
|
||||||
|
"3": "活跃的社区(论坛、Discord),可以提供帮助与协作支持。"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"project": {
|
||||||
|
"title": "项目价值观",
|
||||||
|
"points": {
|
||||||
|
"0": "社区优先:决策由真实玩家/服务器需求引导",
|
||||||
|
"1": "可靠性:我们始终把游戏稳定性、数据同步精度和性能表现放在首位,能优化到的地方都全力以赴。",
|
||||||
|
"2": "开放:欢迎贡献、反馈与透明",
|
||||||
|
"3": "安全:促进公平游戏和尊重互动"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"get_involved": {
|
||||||
|
"title": "参与进来",
|
||||||
|
"description": "参与的方式有很多种——比如加入讨论、反馈问题、贡献代码,或者通过捐赠等途径给项目提供财务支持。",
|
||||||
|
"patreon": "Patreon",
|
||||||
|
"forum": "论坛",
|
||||||
|
"docs": "文档",
|
||||||
|
"github": "GitHub",
|
||||||
|
"discord": "Discord"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"title": "资金与可持续发展",
|
||||||
|
"description": "BeamMP的发展离不开社区的支持。捐款将用于支付服务器等基础设施、带宽、工具及开发时间的成本。如果您认可本项目并愿意支持我们持续成长,请考虑为我们提供捐助。",
|
||||||
|
"patreon": "在Patreon上支持我们",
|
||||||
|
"learn": "了解更多内容请访问GitHub"
|
||||||
|
},
|
||||||
|
"credits": {
|
||||||
|
"title": "致谢与贡献者",
|
||||||
|
"description": "BeamMP项目由模组团队及一众优秀的社区贡献者共同维护。我们同时感谢所有服务器所有者、模组创作者、测试人员以及积极反馈问题的用户——你们的时间与热情是项目持续前进的动力。",
|
||||||
|
"desc_2": "衷心感谢BeamNG.drive的开发团队,他们打造的卓越平台为多人游戏功能的实现奠定了坚实基础。",
|
||||||
|
"desc_3": "同时衷心感谢以下现任与曾经的社区成员,感谢他们多年来对BeamMP项目作出的重大贡献:",
|
||||||
|
"mentions": {
|
||||||
|
"0": "Jojos38(联合创始人)—感谢他早期在设计各方面的工作,帮助将想法变为现实。",
|
||||||
|
"1": "Jetta(jetta.cat)—负责标志的设计与制作。",
|
||||||
|
"2": "Anonymous275 与 Lionkor — 在项目重构期间负责使用 C++ 重写代码的工作。",
|
||||||
|
"3": "Tixx — 感谢他对整个项目代码库作出的重大贡献。",
|
||||||
|
"4": "以及更多未列名的贡献者—衷心感谢每一位以各种方式支持过本项目的朋友!"
|
||||||
|
},
|
||||||
|
"thank_you": "我们正在共同打造一个独特的作品—真诚感谢您成为BeamMP社区的一员!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -65,6 +65,43 @@ const baseRoutes = [
|
|||||||
requiresAuth: false,
|
requiresAuth: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
path: 'privacy',
|
||||||
|
name: 'Privacy',
|
||||||
|
component: () => import('@/views/Privacy.vue'),
|
||||||
|
meta: {
|
||||||
|
title: 'Privacy Policy - BeamMP',
|
||||||
|
description: 'Read our privacy policy',
|
||||||
|
requiresAuth: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'terms',
|
||||||
|
name: 'Terms',
|
||||||
|
component: () => import('@/views/Terms.vue'),
|
||||||
|
meta: {
|
||||||
|
title: 'Terms of Service - BeamMP',
|
||||||
|
description: 'Read our terms of service',
|
||||||
|
requiresAuth: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Redirect route for old keymaster
|
||||||
|
{
|
||||||
|
path: 'keymaster',
|
||||||
|
redirect: (to) => {
|
||||||
|
window.location.replace('https://keymaster.beammp.com/')
|
||||||
|
return `/${to.params.locale || 'en'}`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'k/dashboard',
|
||||||
|
redirect: (to) => {
|
||||||
|
window.location.replace('https://keymaster.beammp.com/')
|
||||||
|
return `/${to.params.locale || 'en'}`
|
||||||
|
},
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
@@ -106,6 +143,12 @@ const router = createRouter({
|
|||||||
|
|
||||||
// Global navigation guard for meta data and locale
|
// Global navigation guard for meta data and locale
|
||||||
router.beforeEach(async (to, from, next) => {
|
router.beforeEach(async (to, from, next) => {
|
||||||
|
// If the path has no locale prefix (e.g. /servers), redirect to /<lang>/servers
|
||||||
|
if (!to.params.locale && to.path !== '/') {
|
||||||
|
const locale = localStorage.getItem('lang') || 'en'
|
||||||
|
return next(`/${locale}${to.path}`)
|
||||||
|
}
|
||||||
|
|
||||||
const paramsLocale = to.params.locale || 'en'
|
const paramsLocale = to.params.locale || 'en'
|
||||||
const i18n = window.i18n
|
const i18n = window.i18n
|
||||||
|
|
||||||
|
|||||||
+7
-6
@@ -1,17 +1,18 @@
|
|||||||
@import "tailwindcss";
|
@import 'tailwindcss';
|
||||||
|
|
||||||
|
@plugin "@tailwindcss/typography";
|
||||||
@plugin "tailwindcss-animate";
|
@plugin "tailwindcss-animate";
|
||||||
@import "tw-animate-css";
|
@import 'tw-animate-css';
|
||||||
|
|
||||||
@custom-variant dark (&:is(.dark *));
|
@custom-variant dark (&:is(.dark *));
|
||||||
|
|
||||||
@theme {
|
@theme {
|
||||||
--color-beammp-orange: #F36D24;
|
--color-beammp-orange: #f36d24;
|
||||||
--color-beammp-white: #FFFFFF;
|
--color-beammp-white: #ffffff;
|
||||||
--color-beammp-black: #000000;
|
--color-beammp-black: #000000;
|
||||||
--color-beammp-gray: #333333;
|
--color-beammp-gray: #333333;
|
||||||
--color-beammp-green: #1D9749;
|
--color-beammp-green: #1d9749;
|
||||||
--color-beammp-blue: #4470B6;
|
--color-beammp-blue: #4470b6;
|
||||||
}
|
}
|
||||||
|
|
||||||
@theme inline {
|
@theme inline {
|
||||||
|
|||||||
@@ -251,7 +251,9 @@ const communities = computed(() => [
|
|||||||
<!-- CTA Section -->
|
<!-- CTA Section -->
|
||||||
<section class="text-center py-16 bg-neutral-50 dark:bg-neutral-900/30 -mx-4 px-4 rounded-xl">
|
<section class="text-center py-16 bg-neutral-50 dark:bg-neutral-900/30 -mx-4 px-4 rounded-xl">
|
||||||
<div class="max-w-2xl mx-auto space-y-6">
|
<div class="max-w-2xl mx-auto space-y-6">
|
||||||
<h2 class="text-3xl md:text-4xl font-bold">{{ $t('message.communities.starting.title') }}</h2>
|
<h2 class="text-3xl md:text-4xl font-bold">
|
||||||
|
{{ $t('message.communities.starting.title') }}
|
||||||
|
</h2>
|
||||||
<p class="text-lg text-neutral-600 dark:text-neutral-400">
|
<p class="text-lg text-neutral-600 dark:text-neutral-400">
|
||||||
{{ $t('message.communities.starting.description') }}
|
{{ $t('message.communities.starting.description') }}
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
+24
-7
@@ -14,13 +14,25 @@ import {
|
|||||||
Rocket,
|
Rocket,
|
||||||
} from 'lucide-vue-next'
|
} from 'lucide-vue-next'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import landingLq from '@/assets/landing-lq.jpg'
|
||||||
|
import landingHq from '@/assets/landing_hq.png'
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
const onlinePlayers = ref('...')
|
const onlinePlayers = ref('...')
|
||||||
const onlineServers = ref('...')
|
const onlineServers = ref('...')
|
||||||
|
const totalServers = ref('...')
|
||||||
const isLoading = ref(true)
|
const isLoading = ref(true)
|
||||||
const heroImageLoaded = ref(false)
|
const heroImageLoaded = ref(false)
|
||||||
const heroImageSrc = ref('/landing-lq.jpg')
|
const heroImageSrc = ref(landingLq)
|
||||||
|
const installerDownloadUrl = computed(() => {
|
||||||
|
const base = import.meta.env.BASE_URL || '/'
|
||||||
|
if (base === './') {
|
||||||
|
return '/installer/BeamMP_Installer.exe'
|
||||||
|
}
|
||||||
|
return `${base.replace(/\/$/, '')}/installer/BeamMP_Installer.exe`
|
||||||
|
|
||||||
|
//return 'https://github.com/BeamMP/BeamMP-Launcher/releases/download/v2.8.0/BeamMP_Installer.exe'
|
||||||
|
})
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
try {
|
try {
|
||||||
@@ -31,19 +43,21 @@ onMounted(async () => {
|
|||||||
if (values.length >= 2) {
|
if (values.length >= 2) {
|
||||||
onlinePlayers.value = values[1]
|
onlinePlayers.value = values[1]
|
||||||
onlineServers.value = values[3]
|
onlineServers.value = values[3]
|
||||||
|
totalServers.value = values[5]
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
onlinePlayers.value = 'N/A'
|
onlinePlayers.value = 'N/A'
|
||||||
onlineServers.value = 'N/A'
|
onlineServers.value = 'N/A'
|
||||||
|
totalServers.value = 'N/A'
|
||||||
} finally {
|
} finally {
|
||||||
isLoading.value = false
|
isLoading.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load high-quality hero image
|
// Load high-quality hero image
|
||||||
const img = new Image()
|
const img = new Image()
|
||||||
img.src = '/landing_hq.png'
|
img.src = landingHq
|
||||||
img.onload = () => {
|
img.onload = () => {
|
||||||
heroImageSrc.value = '/landing_hq.png'
|
heroImageSrc.value = landingHq
|
||||||
heroImageLoaded.value = true
|
heroImageLoaded.value = true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -126,8 +140,8 @@ const devFeatures = computed(() => [
|
|||||||
|
|
||||||
const stats = computed(() => [
|
const stats = computed(() => [
|
||||||
{ label: t('message.home.metrics.active_players'), value: onlinePlayers, suffix: '+' },
|
{ label: t('message.home.metrics.active_players'), value: onlinePlayers, suffix: '+' },
|
||||||
{ label: t('message.home.metrics.public_servers'), value: '500', suffix: '+' },
|
{ label: t('message.home.metrics.public_servers'), value: onlineServers, suffix: '+' },
|
||||||
{ label: t('message.home.metrics.all_servers'), value: '2M', suffix: '+' },
|
{ label: t('message.home.metrics.all_servers'), value: totalServers, suffix: '+' },
|
||||||
])
|
])
|
||||||
const faqs = computed(() => [
|
const faqs = computed(() => [
|
||||||
{
|
{
|
||||||
@@ -188,7 +202,7 @@ const faqs = computed(() => [
|
|||||||
<!-- CTA Buttons -->
|
<!-- CTA Buttons -->
|
||||||
<div class="flex flex-col sm:flex-row gap-4 justify-center items-center pt-4">
|
<div class="flex flex-col sm:flex-row gap-4 justify-center items-center pt-4">
|
||||||
<a
|
<a
|
||||||
href="/installer/BeamMP_Installer.zip"
|
:href="installerDownloadUrl"
|
||||||
download
|
download
|
||||||
class="group flex items-center gap-3 bg-linear-to-r from-beammp-orange to-red-600 hover:from-red-600 hover:to-beammp-orange px-8 py-4 rounded-lg font-semibold text-lg transition-all transform hover:scale-105 shadow-lg hover:shadow-beammp-orange/50 text-white"
|
class="group flex items-center gap-3 bg-linear-to-r from-beammp-orange to-red-600 hover:from-red-600 hover:to-beammp-orange px-8 py-4 rounded-lg font-semibold text-lg transition-all transform hover:scale-105 shadow-lg hover:shadow-beammp-orange/50 text-white"
|
||||||
>
|
>
|
||||||
@@ -217,7 +231,9 @@ const faqs = computed(() => [
|
|||||||
{{ stat.value }} </span
|
{{ stat.value }} </span
|
||||||
>{{ stat.suffix }}
|
>{{ stat.suffix }}
|
||||||
</div>
|
</div>
|
||||||
<div class="text-sm md:text-base text-neutral-700 dark:text-neutral-300 mt-1">{{ stat.label }}</div>
|
<div class="text-sm md:text-base text-neutral-700 dark:text-neutral-300 mt-1">
|
||||||
|
{{ stat.label }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -279,6 +295,7 @@ const faqs = computed(() => [
|
|||||||
</div>
|
</div>
|
||||||
<div class="text-center mt-8">
|
<div class="text-center mt-8">
|
||||||
<RouterLink
|
<RouterLink
|
||||||
|
style="display: none"
|
||||||
:to="`/${$i18n.locale}/communities`"
|
:to="`/${$i18n.locale}/communities`"
|
||||||
class="inline-flex items-center gap-2 bg-beammp-orange hover:bg-beammp-orange/90 text-white px-6 py-3 rounded-lg font-semibold transition-colors"
|
class="inline-flex items-center gap-2 bg-beammp-orange hover:bg-beammp-orange/90 text-white px-6 py-3 rounded-lg font-semibold transition-colors"
|
||||||
>
|
>
|
||||||
|
|||||||
+15
-8
@@ -4,25 +4,32 @@ import { RouterLink } from 'vue-router'
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section class="min-h-[60vh] flex items-center justify-center">
|
<section class="min-h-[60vh] flex items-center justify-center">
|
||||||
<div class="text-center px-6 py-10 sm:px-8 sm:py-12 rounded-xl border border-neutral-200/30 bg-white/60 dark:bg-neutral-900/60 backdrop-blur-md shadow-sm space-y-4">
|
<div
|
||||||
|
class="text-center px-6 py-10 sm:px-8 sm:py-12 rounded-xl border border-neutral-200/30 bg-white/60 dark:bg-neutral-900/60 backdrop-blur-md shadow-sm space-y-4"
|
||||||
|
>
|
||||||
<div class="flex items-center justify-center gap-3">
|
<div class="flex items-center justify-center gap-3">
|
||||||
<span class="inline-flex items-center justify-center rounded-full bg-orange-600/10 text-orange-600 dark:text-orange-400 w-12 h-12 text-xl font-bold">404</span>
|
<span
|
||||||
<h1 class="text-2xl sm:text-3xl font-bold tracking-tight">{{ $t("message.404.title") }}</h1>
|
class="inline-flex items-center justify-center rounded-full bg-orange-600/10 text-orange-600 dark:text-orange-400 w-12 h-12 text-xl font-bold"
|
||||||
|
>404</span
|
||||||
|
>
|
||||||
|
<h1 class="text-2xl sm:text-3xl font-bold tracking-tight">{{ $t('message.404.title') }}</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="text-neutral-600 dark:text-neutral-300 max-w-prose mx-auto">
|
<p class="text-neutral-600 dark:text-neutral-300 max-w-prose mx-auto">
|
||||||
{{ $t("message.404.description") }}
|
{{ $t('message.404.description') }}
|
||||||
</p>
|
</p>
|
||||||
<div class="flex flex-wrap items-center justify-center gap-3 pt-2">
|
<div class="flex flex-wrap items-center justify-center gap-3 pt-2">
|
||||||
<RouterLink
|
<RouterLink
|
||||||
to="/"
|
to="/"
|
||||||
class="inline-flex px-4 py-2 rounded-md bg-orange-600 hover:bg-orange-500 text-white text-sm font-medium">
|
class="inline-flex px-4 py-2 rounded-md bg-orange-600 hover:bg-orange-500 text-white text-sm font-medium"
|
||||||
{{ $t("message.404.return_home") }}
|
>
|
||||||
|
{{ $t('message.404.return_home') }}
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
<RouterLink
|
<RouterLink
|
||||||
to="/servers"
|
to="/servers"
|
||||||
class="inline-flex px-4 py-2 rounded-md border border-neutral-300/60 dark:border-neutral-700/60 text-sm font-medium text-neutral-800 dark:text-neutral-200 hover:bg-neutral-100 dark:hover:bg-neutral-800">
|
class="inline-flex px-4 py-2 rounded-md border border-neutral-300/60 dark:border-neutral-700/60 text-sm font-medium text-neutral-800 dark:text-neutral-200 hover:bg-neutral-100 dark:hover:bg-neutral-800"
|
||||||
{{ $t("message.home.hero.browse_servers") }}
|
>
|
||||||
|
{{ $t('message.home.hero.browse_servers') }}
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ async function loadPartners() {
|
|||||||
// Simple seeded shuffle using the date as seed
|
// Simple seeded shuffle using the date as seed
|
||||||
const shuffled = [...partnersArray]
|
const shuffled = [...partnersArray]
|
||||||
for (let i = shuffled.length - 1; i > 0; i--) {
|
for (let i = shuffled.length - 1; i > 0; i--) {
|
||||||
const j = Math.floor(((seed * (i + 1) * 9301 + 49297) % 233280) / 233280 * (i + 1))
|
const j = Math.floor((((seed * (i + 1) * 9301 + 49297) % 233280) / 233280) * (i + 1))
|
||||||
;[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]
|
;[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +70,10 @@ onMounted(loadPartners)
|
|||||||
:key="idx"
|
:key="idx"
|
||||||
class="shadow-md hover:-translate-y-1 duration-150 group rounded-xl border border-neutral-200 dark:border-neutral-800 dark:bg-neutral-900/50 hover:border-beammp-orange dark:hover:border-beammp-orange hover:shadow-xl transition-all"
|
class="shadow-md hover:-translate-y-1 duration-150 group rounded-xl border border-neutral-200 dark:border-neutral-800 dark:bg-neutral-900/50 hover:border-beammp-orange dark:hover:border-beammp-orange hover:shadow-xl transition-all"
|
||||||
>
|
>
|
||||||
<div class="flex items-center justify-center min-h-16 mb-4 p-6 bg-neutral-900/80 border-b border-beammp-orange border-b-2 rounded-t-xl" style="height: 114px;">
|
<div
|
||||||
|
class="flex items-center justify-center min-h-16 mb-4 p-6 bg-neutral-900/80 border-b border-beammp-orange border-b-2 rounded-t-xl"
|
||||||
|
style="height: 114px"
|
||||||
|
>
|
||||||
<img :src="p.logo" :alt="p.name" class="max-h-16 object-contain" />
|
<img :src="p.logo" :alt="p.name" class="max-h-16 object-contain" />
|
||||||
</div>
|
</div>
|
||||||
<div class="p-6">
|
<div class="p-6">
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref, onMounted } from 'vue'
|
||||||
|
import { marked } from 'marked'
|
||||||
|
|
||||||
|
const content = ref('')
|
||||||
|
const isLoading = ref(true)
|
||||||
|
const error = ref(null)
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
try {
|
||||||
|
const res = await fetch('/docs/privacy_policy.md')
|
||||||
|
if (!res.ok) throw new Error('Failed to load privacy policy')
|
||||||
|
const text = await res.text()
|
||||||
|
content.value = marked.parse(text)
|
||||||
|
} catch (e) {
|
||||||
|
error.value = e.message
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<section class="max-w-4xl mx-auto px-4 py-10">
|
||||||
|
<div v-if="isLoading" class="text-center py-20 text-muted-foreground">Loading...</div>
|
||||||
|
<div v-else-if="error" class="text-center py-20 text-destructive">{{ error }}</div>
|
||||||
|
<article v-else class="prose prose-neutral dark:prose-invert max-w-none" v-html="content" />
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
+9
-28
@@ -2,7 +2,9 @@
|
|||||||
<div class="servers-container">
|
<div class="servers-container">
|
||||||
<div class="servers-header">
|
<div class="servers-header">
|
||||||
<h1 class="text-4xl font-bold">{{ $t('message.servers.title') }}</h1>
|
<h1 class="text-4xl font-bold">{{ $t('message.servers.title') }}</h1>
|
||||||
<p class="text-base servers-subtitle text-neutral-600 dark:text-neutral-400">{{ $t('message.servers.description') }}</p>
|
<p class="text-base servers-subtitle text-neutral-600 dark:text-neutral-400">
|
||||||
|
{{ $t('message.servers.description') }}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="servers-filters">
|
<div class="servers-filters">
|
||||||
@@ -47,7 +49,9 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="server-count">
|
<div class="server-count">
|
||||||
{{ $t('message.servers.server_count', { count: filteredServers.length }) }} ({{ $t('message.servers.players_found', { count: totalPlayers }) }})
|
{{ $t('message.servers.server_count', { count: filteredServers.length }) }} ({{
|
||||||
|
$t('message.servers.players_found', { count: totalPlayers })
|
||||||
|
}})
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -244,8 +248,8 @@ const regionDisplay = computed(() => {
|
|||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/servers.json')
|
//const response = await fetch('/servers.json')
|
||||||
//const response = await fetch('https://backend.beammp.com/servers-info')
|
const response = await fetch('https://backend.beammp.com/servers-info')
|
||||||
console.log(response)
|
console.log(response)
|
||||||
if (!response.ok) throw new Error('Failed to fetch servers')
|
if (!response.ok) throw new Error('Failed to fetch servers')
|
||||||
const data = await response.json()
|
const data = await response.json()
|
||||||
@@ -945,9 +949,7 @@ function joinServer(server) {
|
|||||||
color: var(--text-muted, #666);
|
color: var(--text-muted, #666);
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.dark) .servers-container,
|
:global(.dark) .servers-container {
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
.servers-container {
|
|
||||||
--text-color: #e0e0e0;
|
--text-color: #e0e0e0;
|
||||||
--text-muted: #999999;
|
--text-muted: #999999;
|
||||||
--card-bg: #2a2a2a;
|
--card-bg: #2a2a2a;
|
||||||
@@ -967,7 +969,6 @@ function joinServer(server) {
|
|||||||
--checkbox-hover-shadow: rgba(255, 106, 0, 0.15);
|
--checkbox-hover-shadow: rgba(255, 106, 0, 0.15);
|
||||||
--placeholder-color: #666666;
|
--placeholder-color: #666666;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* Mobile responsive */
|
/* Mobile responsive */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
@@ -1013,23 +1014,3 @@ function joinServer(server) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
/* Light theme defaults */
|
|
||||||
--text-color: #1a1a1a;
|
|
||||||
--text-muted: #666666;
|
|
||||||
--card-bg: #ffffff;
|
|
||||||
--border-color: #e0e0e0;
|
|
||||||
--input-bg: #ffffff;
|
|
||||||
--tag-bg: #e8e8e8;
|
|
||||||
--primary-color: #5d9cec;
|
|
||||||
--header-bg: #f5f5f5;
|
|
||||||
--hover-bg: #f9f9f9;
|
|
||||||
--active-bg: #f0f7ff;
|
|
||||||
--details-bg: #fafafa;
|
|
||||||
--filters-bg: linear-gradient(135deg, rgba(255, 255, 255, 0.9) 0%, rgba(249, 249, 249, 0.95) 100%);
|
|
||||||
--filters-border: var(--border-color, #e0e0e0);
|
|
||||||
--filters-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
|
||||||
--checkbox-bg: #ffffff;
|
|
||||||
--checkbox-border: #d0d0d0;
|
|
||||||
--checkbox-hover-shadow: rgba(255, 106, 0, 0.1);
|
|
||||||
--placeholder-color: var(--text-muted, #999999);
|
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref, onMounted } from 'vue'
|
||||||
|
import { marked } from 'marked'
|
||||||
|
|
||||||
|
const content = ref('')
|
||||||
|
const isLoading = ref(true)
|
||||||
|
const error = ref(null)
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
try {
|
||||||
|
const res = await fetch('/docs/terms.md')
|
||||||
|
if (!res.ok) throw new Error('Failed to load terms of service')
|
||||||
|
const text = await res.text()
|
||||||
|
content.value = marked.parse(text)
|
||||||
|
} catch (e) {
|
||||||
|
error.value = e.message
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<section class="max-w-4xl mx-auto px-4 py-10">
|
||||||
|
<div v-if="isLoading" class="text-center py-20 text-muted-foreground">Loading...</div>
|
||||||
|
<div v-else-if="error" class="text-center py-20 text-destructive">{{ error }}</div>
|
||||||
|
<article v-else class="prose prose-neutral dark:prose-invert max-w-none" v-html="content" />
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
Reference in New Issue
Block a user