Compare commits
101 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9066e31c55 | |||
| d19a10c08b | |||
| a90e60ef32 | |||
| 070fb38709 | |||
| ed1cb0e2a8 | |||
| bedcd0cb50 | |||
| 2987de52ec | |||
| aaf888f182 | |||
| 052da9316c | |||
| 842e229e55 | |||
| b2742428d0 | |||
| 48717eeabd | |||
| 5f14414055 | |||
| a253919575 | |||
| 4626a69185 | |||
| 44268d076d | |||
| c2e460b408 | |||
| c827c81d28 | |||
| 5f9aad80ac | |||
| be9bae93de | |||
| 5295ec664b | |||
| f59b5df887 | |||
| cf3c3790b5 | |||
| 4331a58e50 | |||
| 214ccd10d1 | |||
| c6fb3331c2 | |||
| 391c8bc14e | |||
| 84d63cb84d | |||
| e95c68165a | |||
| 070e31dbec | |||
| c9fc193aa3 | |||
| e7f80d83a6 | |||
| 9d9c10179c | |||
| e2a1b4a598 | |||
| ca7b7e9d1b | |||
| 7e53a63455 | |||
| f2aec71b6e | |||
| c317aa4e37 | |||
| df371568c7 | |||
| 6ed1bf7352 | |||
| 31a179bc45 | |||
| cf3c13c303 | |||
| 476211b220 | |||
| 625e5fd596 | |||
| 7f8633ab09 | |||
| 176b5c2934 | |||
| cf53167cec | |||
| 171bb3f018 | |||
| 6e6470086e | |||
| b171d836ad | |||
| f3315d443f | |||
| c09b0cc69f | |||
| 88a3f10192 | |||
| 952d35535f | |||
| 600123dd8b | |||
| b3d6f59aaf | |||
| 07a84f25d5 | |||
| f3269d0bee | |||
| cdbf8f991c | |||
| 04fb9ba50f | |||
| 2d83ba9dfd | |||
| c0a10c4710 | |||
| 1e8489018d | |||
| ab6c93791f | |||
| acc1377bbb | |||
| 3873388b48 | |||
| 0d4d34634a | |||
| d5b7c52641 | |||
| 9c85fb2f10 | |||
| a2d2cb07d4 | |||
| 403489e43c | |||
| d24ec58185 | |||
| a3f47c5b12 | |||
| d5c33e56e5 | |||
| 266c1470ba | |||
| 6fcf1d1f6f | |||
| 30eb6b96e0 | |||
| ebb7611a92 | |||
| 77c71a501f | |||
| c5988f94b0 | |||
| 1671b775e4 | |||
| 4d8c30f48c | |||
| d1150ed84d | |||
| 5dfb7b79d7 | |||
| 19abd94069 | |||
| c0f96b35de | |||
| dedc16fb73 | |||
| f8d4478d35 | |||
| 738721119e | |||
| b8a5db6d48 | |||
| cc48fe2ffc | |||
| a307a3cd9f | |||
| 9a2a10499d | |||
| 3daa559a67 | |||
| e459538f18 | |||
| 0065093ca3 | |||
| 391f634d65 | |||
| 838a33b4d5 | |||
| 647ab8bda9 | |||
| 8b37e854d8 | |||
| 23ec17460e |
@@ -0,0 +1,34 @@
|
|||||||
|
name: Build Docker image and push to release
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
docker:
|
||||||
|
if: "!github.event.release.prerelease"
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Connect to Tailscale
|
||||||
|
uses: tailscale/github-action@v4
|
||||||
|
with:
|
||||||
|
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
|
||||||
|
oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}
|
||||||
|
tags: tag:ci
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
with:
|
||||||
|
driver: docker
|
||||||
|
- name: Login to Docker Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ secrets.REGISTRY_URL }}
|
||||||
|
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||||
|
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
push: true
|
||||||
|
tags: ${{ secrets.REGISTRY_URL }}/beammp/website:${{ github.REF_NAME }}, ${{ secrets.REGISTRY_URL }}/beammp/website:latest, ${{ secrets.REGISTRY_URL }}/beammp/website:production
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
name: Build Docker image and push to prerelease
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
docker:
|
||||||
|
if: github.event.release.prerelease
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Connect to Tailscale
|
||||||
|
uses: tailscale/github-action@v4
|
||||||
|
with:
|
||||||
|
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
|
||||||
|
oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}
|
||||||
|
tags: tag:ci
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
with:
|
||||||
|
driver: docker
|
||||||
|
- name: Login to Docker Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ secrets.REGISTRY_URL }}
|
||||||
|
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||||
|
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
push: true
|
||||||
|
tags: ${{ secrets.REGISTRY_URL }}/beammp/website:${{ github.REF_NAME }}, ${{ secrets.REGISTRY_URL }}/beammp/website:latest
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
*.log
|
||||||
|
.DS_Store
|
||||||
|
pnpm-lock.yaml
|
||||||
|
package-lock.json
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"semi": false,
|
||||||
|
"singleQuote": true,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"useTabs": false,
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"printWidth": 100,
|
||||||
|
"arrowParens": "always",
|
||||||
|
"endOfLine": "lf",
|
||||||
|
"vueIndentScriptAndStyle": false
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"cSpell.words": [
|
||||||
|
"beammp",
|
||||||
|
"beammpservers",
|
||||||
|
"beamng",
|
||||||
|
"Deutsch",
|
||||||
|
"Español",
|
||||||
|
"Français",
|
||||||
|
"freeroam",
|
||||||
|
"gridlines",
|
||||||
|
"Italiano",
|
||||||
|
"Lucide",
|
||||||
|
"maxplayers",
|
||||||
|
"modlist",
|
||||||
|
"modstotal",
|
||||||
|
"modstotalsize",
|
||||||
|
"Offroad",
|
||||||
|
"playerslist",
|
||||||
|
"reka",
|
||||||
|
"rels",
|
||||||
|
"roleplay",
|
||||||
|
"sdesc",
|
||||||
|
"sname",
|
||||||
|
"vueuse",
|
||||||
|
"Русский"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,25 +1,30 @@
|
|||||||
FROM node:18.16.0-alpine3.17
|
# Step 1: Build stage
|
||||||
|
FROM node:lts-alpine AS build
|
||||||
|
|
||||||
# Create app directory
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN npm ci
|
||||||
|
COPY . .
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
RUN apk --no-cache add curl
|
# Step 2: Serve stage
|
||||||
|
FROM nginx:stable
|
||||||
|
|
||||||
# Install app dependencies
|
# Remove default nginx static assets
|
||||||
# A wildcard is used to ensure both package.json AND package-lock.json are copied
|
RUN rm -rf /usr/share/nginx/html/*
|
||||||
# where available (npm@5+)
|
|
||||||
COPY package*.json /app
|
|
||||||
|
|
||||||
# General Install of Deps
|
# Copy built files from the previous stage
|
||||||
# RUN npm install
|
COPY --from=build /app/dist /usr/share/nginx/html
|
||||||
# If you are building your code for production
|
|
||||||
RUN npm ci --only=production
|
|
||||||
|
|
||||||
# Bundle app source
|
# Copy secure nginx configs
|
||||||
COPY . /app
|
COPY nginx.main.conf /etc/nginx/nginx.conf
|
||||||
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
EXPOSE 3599
|
# Use non-root user for security
|
||||||
|
USER nginx
|
||||||
|
|
||||||
HEALTHCHECK CMD curl --fail http://localhost:3599/ping || exit 1
|
# Expose port 80
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
CMD [ "node", "index.js" ]
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
|
|||||||
@@ -1 +1,112 @@
|
|||||||
# BeamMP-Website
|
# BeamMP Website
|
||||||
|
|
||||||
|
This repository is home for the BeamMP website. The website features support for translations too now!
|
||||||
|
|
||||||
|
**Tech Stack**
|
||||||
|
- **Framework:** Vue 3 (`vue`, `vue-router`)
|
||||||
|
- **Build:** Vite 7 (`vite`, `@vitejs/plugin-vue`)
|
||||||
|
- **Styling:** Tailwind CSS 4 + `tailwindcss-animate`, `tailwind-merge`
|
||||||
|
- **UI Icons/Utils:** `lucide-vue-next`, `clsx`, `class-variance-authority`
|
||||||
|
- **Radix-style Components:** `reka-ui` through `shadcn-vue`
|
||||||
|
- **Composable utilities:** `@vueuse/core`
|
||||||
|
|
||||||
|
**Theming and Colour Guide**
|
||||||
|
- BeamMP Orange `#F36D24`
|
||||||
|
- BeamMP Blue `#4470B6`
|
||||||
|
- BeamMP Green `#1D9749`
|
||||||
|
- Gray `#333333`
|
||||||
|
- Black `#000000`
|
||||||
|
- White `#FFFFFF`
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
Prerequisites:
|
||||||
|
- Node.js 22+
|
||||||
|
- pnpm, npm, or yarn (examples use npm)
|
||||||
|
|
||||||
|
Install dependencies and run the dev server:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# from this folder (repo root)
|
||||||
|
npm install
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Build for production and preview locally:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
npm run build
|
||||||
|
npm run preview
|
||||||
|
```
|
||||||
|
|
||||||
|
Vite will print the local dev URL (usually `http://localhost:5173`).
|
||||||
|
|
||||||
|
## Project Scripts
|
||||||
|
- `npm run dev`: Start Vite development server
|
||||||
|
- `npm run build`: Production build
|
||||||
|
- `npm run preview`: Preview the production build locally
|
||||||
|
- `npm run lint`: Lint and auto-fix Vue/JS files with ESLint
|
||||||
|
- `npm run format`: Format all source files with Prettier
|
||||||
|
|
||||||
|
## Directory Overview
|
||||||
|
- `src/` – App source (styles, components, pages, routing)
|
||||||
|
- `src/components/ui/` – UI components (e.g., `button`, `navigation-menu`)
|
||||||
|
- `src/lib/` – Utilities and helpers
|
||||||
|
- `routes/` - vue-router routes
|
||||||
|
- `views/` - Pages of the website
|
||||||
|
- `index.html` – Vite HTML entry
|
||||||
|
- `tailwind.config.js` – Tailwind configuration
|
||||||
|
- `vite.config.js` – Vite configuration
|
||||||
|
- `components.json` – Shadcn-Vue component registry
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
We welcome contributions! Here’s how to get started.
|
||||||
|
|
||||||
|
### 1) Pick an Issue or Open One
|
||||||
|
- Browse issues or propose an enhancement/bug. Share context and repro steps.
|
||||||
|
|
||||||
|
### 2) Create a Branch
|
||||||
|
- Use a descriptive branch name:
|
||||||
|
- `feature/<short-description>`
|
||||||
|
- `fix/<short-description>`
|
||||||
|
- `docs/<short-description>`
|
||||||
|
|
||||||
|
### 3) Dev Environment
|
||||||
|
- Install deps with `npm install` and run `npm run dev`.
|
||||||
|
- Keep changes scoped and focused; avoid drive-by refactors unless agreed.
|
||||||
|
|
||||||
|
### 4) Code Style & Patterns
|
||||||
|
- **Vue 3 + `<script setup>`** preferred for new components.
|
||||||
|
- **Linting**: Run `npm run lint` before committing to catch errors. ESLint is configured for Vue 3 best practices.
|
||||||
|
- **Formatting**: Run `npm run format` to auto-format code with Prettier (or use an editor extension).
|
||||||
|
- **Type safety**: If/when TypeScript is introduced, prefer explicit props and emits.
|
||||||
|
- **Tailwind**: Use utility classes; extract variants with `class-variance-authority` when patterns repeat.
|
||||||
|
- **Accessibility**: Prefer accessible primitives (e.g., `reka-ui`) and keyboard support.
|
||||||
|
- **File naming**: `PascalCase.vue` for components; avoid one-letter variable names.
|
||||||
|
|
||||||
|
### 5) Testing & Checks
|
||||||
|
- Run the app locally and verify core flows.
|
||||||
|
- Ensure components render on both light/dark themes if relevant.
|
||||||
|
- If you add dependencies, update this README and relevant configs.
|
||||||
|
|
||||||
|
### 6) Commit & PR
|
||||||
|
- Write clear, imperative commit messages, e.g., `feat: add NavigationMenu component`.
|
||||||
|
- Open a PR describing the problem, solution, screenshots if UI changes, and any follow-ups.
|
||||||
|
- Link related issues. Keep PRs small; big changes should be split.
|
||||||
|
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
BeamMP makes an effort to be maintained for multiple languages.
|
||||||
|
The current progress of this sits at:
|
||||||
|
[](https://gitlocalize.com/repo/10617?utm_source=badge)
|
||||||
|
We use [GitLocalize](https://gitlocalize.com/) for managing this. You can contribute if you wish here: https://gitlocalize.com/repo/10617.
|
||||||
|
|
||||||
|
The individual language progress is as follows:
|
||||||
|
|
||||||
|
| Language | Badge |
|
||||||
|
|-----------------------|---------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| French | [](https://gitlocalize.com/repo/10617/fr?utm_source=badge) |
|
||||||
|
| German | [](https://gitlocalize.com/repo/10617/de?utm_source=badge) |
|
||||||
|
| Italian | [](https://gitlocalize.com/repo/10617/it?utm_source=badge) |
|
||||||
|
| Russian | [](https://gitlocalize.com/repo/10617/ru?utm_source=badge) |
|
||||||
|
| Spanish | [](https://gitlocalize.com/repo/10617/es?utm_source=badge) |
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://shadcn-vue.com/schema.json",
|
||||||
|
"style": "new-york",
|
||||||
|
"typescript": false,
|
||||||
|
"tailwind": {
|
||||||
|
"config": "tailwind.config.js",
|
||||||
|
"css": "src/style.css",
|
||||||
|
"baseColor": "gray",
|
||||||
|
"cssVariables": true,
|
||||||
|
"prefix": ""
|
||||||
|
},
|
||||||
|
"iconLibrary": "lucide",
|
||||||
|
"aliases": {
|
||||||
|
"components": "@/components",
|
||||||
|
"utils": "@/lib/utils",
|
||||||
|
"ui": "@/components/ui",
|
||||||
|
"lib": "@/lib",
|
||||||
|
"composables": "@/composables"
|
||||||
|
},
|
||||||
|
"registries": {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
services:
|
||||||
|
website:
|
||||||
|
build:
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: website
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:3000:80"
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
import pluginVue from 'eslint-plugin-vue'
|
||||||
|
import js from '@eslint/js'
|
||||||
|
import prettier from 'eslint-plugin-prettier'
|
||||||
|
import configPrettier from 'eslint-config-prettier'
|
||||||
|
|
||||||
|
export default [
|
||||||
|
// Ignore patterns (replaces .eslintignore)
|
||||||
|
{
|
||||||
|
ignores: ['node_modules', 'dist', '*.log', '.DS_Store'],
|
||||||
|
},
|
||||||
|
|
||||||
|
// Base JavaScript config
|
||||||
|
js.configs.recommended,
|
||||||
|
|
||||||
|
// Vue 3 recommended config
|
||||||
|
...pluginVue.configs['flat/recommended'],
|
||||||
|
|
||||||
|
// Global configuration
|
||||||
|
{
|
||||||
|
plugins: {
|
||||||
|
prettier,
|
||||||
|
},
|
||||||
|
languageOptions: {
|
||||||
|
ecmaVersion: 'latest',
|
||||||
|
sourceType: 'module',
|
||||||
|
globals: {
|
||||||
|
// Browser globals
|
||||||
|
window: 'readonly',
|
||||||
|
document: 'readonly',
|
||||||
|
navigator: 'readonly',
|
||||||
|
console: 'readonly',
|
||||||
|
localStorage: 'readonly',
|
||||||
|
fetch: 'readonly',
|
||||||
|
alert: 'readonly',
|
||||||
|
prompt: 'readonly',
|
||||||
|
getComputedStyle: 'readonly',
|
||||||
|
// Node globals
|
||||||
|
process: 'readonly',
|
||||||
|
__dirname: 'readonly',
|
||||||
|
__filename: 'readonly',
|
||||||
|
module: 'readonly',
|
||||||
|
require: 'readonly',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
// Prettier integration
|
||||||
|
...configPrettier.rules,
|
||||||
|
'prettier/prettier': 'error',
|
||||||
|
|
||||||
|
// Vue-specific rules
|
||||||
|
'vue/multi-word-component-names': 'off',
|
||||||
|
'vue/no-v-html': 'warn',
|
||||||
|
'vue/require-default-prop': 'off',
|
||||||
|
'vue/require-prop-types': 'warn',
|
||||||
|
'vue/component-name-in-template-casing': ['error', 'PascalCase'],
|
||||||
|
'vue/html-self-closing': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
html: {
|
||||||
|
void: 'always',
|
||||||
|
normal: 'always',
|
||||||
|
component: 'always',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
// General JavaScript rules
|
||||||
|
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||||
|
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||||||
|
'prefer-const': 'error',
|
||||||
|
'no-var': 'error',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
INSTANCES=3
|
|
||||||
DEBUG=false
|
|
||||||
PORT=3000
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
# Vue 3 + Vite
|
|
||||||
|
|
||||||
This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
|
||||||
|
|
||||||
## Recommended IDE Setup
|
|
||||||
|
|
||||||
- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>BeamMP</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="app"></div>
|
|
||||||
<script type="module" src="/src/main.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "beammp-website-frontend",
|
|
||||||
"private": true,
|
|
||||||
"version": "0.0.0",
|
|
||||||
"type": "module",
|
|
||||||
"scripts": {
|
|
||||||
"dev": "vite",
|
|
||||||
"build": "vite build",
|
|
||||||
"preview": "vite preview"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@headlessui/vue": "^1.7.16",
|
|
||||||
"@heroicons/vue": "^2.0.18",
|
|
||||||
"@statnett/vue-plotly": "^0.3.2",
|
|
||||||
"axios": "^1.6.2",
|
|
||||||
"plotly.js": "^2.27.1",
|
|
||||||
"plotly.js-dist": "^2.27.1",
|
|
||||||
"plotly.js-dist-min": "^2.27.1",
|
|
||||||
"uuid": "^9.0.1",
|
|
||||||
"vue": "^3.3.8",
|
|
||||||
"vue-router": "^4.2.5"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@vitejs/plugin-vue": "^4.5.0",
|
|
||||||
"autoprefixer": "^10.4.16",
|
|
||||||
"postcss": "^8.4.32",
|
|
||||||
"tailwindcss": "^3.3.6",
|
|
||||||
"vite": "^5.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
export default {
|
|
||||||
plugins: {
|
|
||||||
tailwindcss: {},
|
|
||||||
autoprefixer: {},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -1,10 +0,0 @@
|
|||||||
<script setup>
|
|
||||||
import Navbar from '@/components/NavBar.vue'
|
|
||||||
import Footer from '@/components/Footer.vue'
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<Navbar />
|
|
||||||
<router-view />
|
|
||||||
<Footer />
|
|
||||||
</template>
|
|
||||||
|
Before Width: | Height: | Size: 4.0 MiB |
|
Before Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 5.2 KiB |
@@ -1,121 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="bg-gray-800 text-gray-400">
|
|
||||||
<div class="px-4 pt-16 mx-auto md:max-w-full lg:max-w-screen-xl md:px-24 lg:px-8">
|
|
||||||
<div class="grid gap-16 row-gap-10 mb-8 lg:grid-cols-6">
|
|
||||||
<div class="md:max-w-md lg:col-span-2">
|
|
||||||
<a href="/" aria-label="Go home" title="Company" class="inline-flex items-center">
|
|
||||||
<img class="h-8 w-auto" src="../assets/beammp-logo.png" alt="BeamMP Logo" />
|
|
||||||
<span class="ml-4 text-xl font-bold tracking-wide text-gray-200 uppercase">BeamMP</span>
|
|
||||||
</a>
|
|
||||||
<div class="mt-4 lg:max-w-sm">
|
|
||||||
<p class="text-sm text-gray-300">
|
|
||||||
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam.
|
|
||||||
</p>
|
|
||||||
<p class="mt-4 text-sm text-gray-300">
|
|
||||||
Eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="grid grid-cols-2 gap-5 row-gap-8 lg:col-span-4 md:grid-cols-4">
|
|
||||||
<div>
|
|
||||||
<p class="font-semibold tracking-wide text-gray-200">Sites</p>
|
|
||||||
<ul class="mt-2 space-y-2">
|
|
||||||
<li>
|
|
||||||
<a href="https://patreon.com/beammp" class="text-gray-300 transition-colors duration-300 hover:text-deep-purple-accent-400">Patreon</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="https://forum.beammp.com/" class="text-gray-300 transition-colors duration-300 hover:text-deep-purple-accent-400">Forum</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="https://keymaster.beammp.com/" class="text-gray-300 transition-colors duration-300 hover:text-deep-purple-accent-400">Keymaster</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="https://docs.beammp.com/" class="text-gray-300 transition-colors duration-300 hover:text-deep-purple-accent-400">Docs</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="https://store.beammp.com/" class="text-gray-300 transition-colors duration-300 hover:text-deep-purple-accent-400">Store</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p class="font-semibold tracking-wide text-gray-200">GitHub</p>
|
|
||||||
<ul class="mt-2 space-y-2">
|
|
||||||
<li>
|
|
||||||
<a href="https://github.com/BeamMP/BeamMP" class="text-gray-300 transition-colors duration-300 hover:text-deep-purple-accent-400">Mod</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="https://github.com/BeamMP/BeamMP-Launcher" class="text-gray-300 transition-colors duration-300 hover:text-deep-purple-accent-400">Launcher</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="https://github.com/BeamMP/BeamMP-Server" class="text-gray-300 transition-colors duration-300 hover:text-deep-purple-accent-400">Server</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p class="font-semibold tracking-wide text-gray-200">?</p>
|
|
||||||
<ul class="mt-2 space-y-2">
|
|
||||||
<li>
|
|
||||||
<a href="/" class="text-gray-300 transition-colors duration-300 hover:text-deep-purple-accent-400">?</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="/" class="text-gray-300 transition-colors duration-300 hover:text-deep-purple-accent-400">?</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="/" class="text-gray-300 transition-colors duration-300 hover:text-deep-purple-accent-400">?</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="/" class="text-gray-300 transition-colors duration-300 hover:text-deep-purple-accent-400">?</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="/" class="text-gray-300 transition-colors duration-300 hover:text-deep-purple-accent-400">?</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p class="font-semibold tracking-wide text-gray-200">Documents</p>
|
|
||||||
<ul class="mt-2 space-y-2">
|
|
||||||
<li>
|
|
||||||
<a href="/rules" class="text-gray-300 transition-colors duration-300 hover:text-deep-purple-accent-400">Rules</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="/privacy" class="text-gray-300 transition-colors duration-300 hover:text-deep-purple-accent-400">Privacy policy</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="/terms" class="text-gray-300 transition-colors duration-300 hover:text-deep-purple-accent-400">Terms</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-col justify-between pt-5 pb-10 border-t sm:flex-row">
|
|
||||||
<p class="text-sm text-gray-300">
|
|
||||||
© Copyright 2024 BeamMP. All rights reserved.
|
|
||||||
</p>
|
|
||||||
<div class="flex items-center mt-4 space-x-4 sm:mt-0">
|
|
||||||
<a href="/" class="text-gray-400 transition-colors duration-300 hover:text-deep-purple-accent-400">
|
|
||||||
<svg viewBox="0 0 24 24" fill="currentColor" class="h-5">
|
|
||||||
<path
|
|
||||||
d="M24 4.6c-0.9 0.4-1.8 0.7-2.8 0.8c1-0.6 1.8-1.6 2.2-2.7c-1 0.6-2 1-3.1 1.2c-0.9-1-2.2-1.6-3.6-1.6 c-2.7 0-4.9 2.2-4.9 4.9c0 0.4 0 0.8 0.1 1.1C7.7 8.1 4.1 6.1 1.7 3.1C1.2 3.9 1 4.7 1 5.6c0 1.7 0.9 3.2 2.2 4.1 C2.4 9.7 1.6 9.5 1 9.1c0 0 0 0 0 0.1c0 2.4 1.7 4.4 3.9 4.8c-0.4 0.1-0.8 0.2-1.3 0.2c-0.3 0-0.6 0-0.9-0.1c0.6 2 2.4 3.4 4.6 3.4 c-1.7 1.3-3.8 2.1-6.1 2.1c-0.4 0-0.8 0-1.2-0.1c2.2 1.4 4.8 2.2 7.5 2.2c9.1 0 14-7.5 14-14c0-0.2 0-0.4 0-0.6 C22.5 6.4 23.3 5.5 24 4.6z"
|
|
||||||
></path>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
<a href="/" class="text-gray-400 transition-colors duration-300 hover:text-deep-purple-accent-400">
|
|
||||||
<svg viewBox="0 0 30 30" fill="currentColor" class="h-6">
|
|
||||||
<circle cx="15" cy="15" r="4"></circle>
|
|
||||||
<path
|
|
||||||
d="M19.999 3h-10C6.14 3 3 6.141 3 10.001v10C3 23.86 6.141 27 10.001 27h10C23.86 27 27 23.859 27 19.999v-10 C27 6.14 23.859 3 19.999 3z M15 21c-3.309 0-6-2.691-6-6s2.691-6 6-6s6 2.691 6 6S18.309 21 15 21z M22 9c-0.552 0-1-0.448-1-1 c0-0.552 0.448-1 1-1s1 0.448 1 1C23 8.552 22.552 9 22 9z"
|
|
||||||
></path>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
<a href="/" class="text-gray-400 transition-colors duration-300 hover:text-deep-purple-accent-400">
|
|
||||||
<svg viewBox="0 0 24 24" fill="currentColor" class="h-5">
|
|
||||||
<path
|
|
||||||
d="M22 0H2C0.895 0 0 0.895 0 2v20c0 1.105 0.895 2 2 2h11v-9h-3v-4h3V8.413c0-3.1 1.893-4.788 4.659-4.788 c1.325 0 2.463 0.099 2.795 0.143v3.24l-1.918 0.001c-1.504 0-1.795 0.715-1.795 1.763V11h4.44l-1 4h-3.44v9H22c1.105 0 2-0.895 2-2 V2C24 0.895 23.105 0 22 0z"
|
|
||||||
></path>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@@ -1,133 +0,0 @@
|
|||||||
<template>
|
|
||||||
<Disclosure as="nav" class="bg-gray-800" v-slot="{ open }">
|
|
||||||
<div class="mx-auto max-w-7xl px-2 sm:px-6 lg:px-8">
|
|
||||||
<div class="relative flex h-16 items-center justify-between">
|
|
||||||
<div class="absolute inset-y-0 left-0 flex items-center sm:hidden">
|
|
||||||
<!-- Mobile menu button-->
|
|
||||||
<DisclosureButton
|
|
||||||
class="relative inline-flex items-center justify-center rounded-md p-2 text-gray-400 hover:bg-gray-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white">
|
|
||||||
<span class="absolute -inset-0.5" />
|
|
||||||
<span class="sr-only">Open main menu</span>
|
|
||||||
<Bars3Icon v-if="!open" class="block h-6 w-6" aria-hidden="true" />
|
|
||||||
<XMarkIcon v-else class="block h-6 w-6" aria-hidden="true" />
|
|
||||||
</DisclosureButton>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-1 items-center justify-center sm:items-stretch sm:justify-start">
|
|
||||||
<div class="flex flex-shrink-0 items-center">
|
|
||||||
<a href="/"><img class="h-8 w-auto" src="../assets/beammp-logo.png" alt="BeamMP Logo" /></a>
|
|
||||||
</div>
|
|
||||||
<div class="hidden sm:ml-6 sm:block">
|
|
||||||
<div class="flex space-x-4">
|
|
||||||
<a v-for="item in navigation" :key="item.name" :href="item.href"
|
|
||||||
:class="[item.current ? 'bg-gray-900 text-white' : 'text-gray-300 hover:bg-gray-700 hover:text-white', 'rounded-md px-3 py-2 text-sm font-medium']"
|
|
||||||
:aria-current="item.current ? 'page' : undefined">{{ item.name }}</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Mode Switcher Dropdown -->
|
|
||||||
<div class="flex items-center border-l border-slate-200 ml-6 pl-6 dark:border-slate-800">
|
|
||||||
<label class="sr-only" id="headlessui-listbox-label-:Rpkcr6:" data-headlessui-state="">Theme</label>
|
|
||||||
<button type="button" id="headlessui-listbox-button-:R19kcr6:" aria-haspopup="true" aria-expanded="false" data-headlessui-state="" aria-labelledby="headlessui-listbox-label-:Rpkcr6: headlessui-listbox-button-:R19kcr6:" class="">
|
|
||||||
<span class="dark:hidden">
|
|
||||||
<svg viewBox="0 0 24 24" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-6 h-6">
|
|
||||||
<path d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" class="stroke-slate-400 dark:stroke-slate-500"></path>
|
|
||||||
<path
|
|
||||||
d="M12 4v1M17.66 6.344l-.828.828M20.005 12.004h-1M17.66 17.664l-.828-.828M12 20.01V19M6.34 17.664l.835-.836M3.995 12.004h1.01M6 6l.835.836"
|
|
||||||
class="stroke-slate-400 dark:stroke-slate-500"></path>
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
<span class="hidden dark:inline">
|
|
||||||
<svg viewBox="0 0 24 24" fill="none" class="w-6 h-6">
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
|
||||||
d="M17.715 15.15A6.5 6.5 0 0 1 9 6.035C6.106 6.922 4 9.645 4 12.867c0 3.94 3.153 7.136 7.042 7.136 3.101 0 5.734-2.032 6.673-4.853Z"
|
|
||||||
class="fill-transparent"></path>
|
|
||||||
<path
|
|
||||||
d="m17.715 15.15.95.316a1 1 0 0 0-1.445-1.185l.495.869ZM9 6.035l.846.534a1 1 0 0 0-1.14-1.49L9 6.035Zm8.221 8.246a5.47 5.47 0 0 1-2.72.718v2a7.47 7.47 0 0 0 3.71-.98l-.99-1.738Zm-2.72.718A5.5 5.5 0 0 1 9 9.5H7a7.5 7.5 0 0 0 7.5 7.5v-2ZM9 9.5c0-1.079.31-2.082.845-2.93L8.153 5.5A7.47 7.47 0 0 0 7 9.5h2Zm-4 3.368C5 10.089 6.815 7.75 9.292 6.99L8.706 5.08C5.397 6.094 3 9.201 3 12.867h2Zm6.042 6.136C7.718 19.003 5 16.268 5 12.867H3c0 4.48 3.588 8.136 8.042 8.136v-2Zm5.725-4.17c-.81 2.433-3.074 4.17-5.725 4.17v2c3.552 0 6.553-2.327 7.622-5.537l-1.897-.632Z"
|
|
||||||
class="fill-slate-400 dark:fill-slate-500"></path>
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
|
||||||
d="M17 3a1 1 0 0 1 1 1 2 2 0 0 0 2 2 1 1 0 1 1 0 2 2 2 0 0 0-2 2 1 1 0 1 1-2 0 2 2 0 0 0-2-2 1 1 0 1 1 0-2 2 2 0 0 0 2-2 1 1 0 0 1 1-1Z"
|
|
||||||
class="fill-slate-400 dark:fill-slate-500"></path>
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!--<div class="absolute inset-y-0 right-0 flex items-center pr-2 sm:static sm:inset-auto sm:ml-6 sm:pr-0">
|
|
||||||
<button type="button" class="relative rounded-full bg-gray-800 p-1 text-gray-400 hover:text-white focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800">
|
|
||||||
<span class="absolute -inset-1.5" />
|
|
||||||
<span class="sr-only">View notifications</span>
|
|
||||||
<BellIcon class="h-6 w-6" aria-hidden="true" />
|
|
||||||
</button>-->
|
|
||||||
|
|
||||||
<!-- Profile dropdown - From the Forum Accounts System -->
|
|
||||||
<!--<Menu as="div" class="relative ml-3">
|
|
||||||
<div>
|
|
||||||
<MenuButton class="relative flex rounded-full bg-gray-800 text-sm focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800">
|
|
||||||
<span class="absolute -inset-1.5" />
|
|
||||||
<span class="sr-only">Open user menu</span>
|
|
||||||
<img class="h-8 w-8 rounded-full" src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt="" />
|
|
||||||
</MenuButton>
|
|
||||||
</div>
|
|
||||||
<transition enter-active-class="transition ease-out duration-100" enter-from-class="transform opacity-0 scale-95" enter-to-class="transform opacity-100 scale-100" leave-active-class="transition ease-in duration-75" leave-from-class="transform opacity-100 scale-100" leave-to-class="transform opacity-0 scale-95">
|
|
||||||
<MenuItems class="absolute right-0 z-10 mt-2 w-48 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
|
|
||||||
<MenuItem v-slot="{ active }">
|
|
||||||
<a href="#" :class="[active ? 'bg-gray-100' : '', 'block px-4 py-2 text-sm text-gray-700']">Your Profile</a>
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem v-slot="{ active }">
|
|
||||||
<a href="#" :class="[active ? 'bg-gray-100' : '', 'block px-4 py-2 text-sm text-gray-700']">Settings</a>
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem v-slot="{ active }">
|
|
||||||
<a href="#" :class="[active ? 'bg-gray-100' : '', 'block px-4 py-2 text-sm text-gray-700']">Sign out</a>
|
|
||||||
</MenuItem>
|
|
||||||
</MenuItems>
|
|
||||||
</transition>
|
|
||||||
</Menu>-->
|
|
||||||
<!--</div>-->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<DisclosurePanel class="sm:hidden">
|
|
||||||
<div class="space-y-1 px-2 pb-3 pt-2">
|
|
||||||
<DisclosureButton v-for="item in navigation" :key="item.name" as="a" :href="item.href"
|
|
||||||
:class="[item.current ? 'bg-gray-900 text-white' : 'text-gray-300 hover:bg-gray-700 hover:text-white', 'block rounded-md px-3 py-2 text-base font-medium']"
|
|
||||||
:aria-current="item.current ? 'page' : undefined">{{ item.name }}</DisclosureButton>
|
|
||||||
</div>
|
|
||||||
</DisclosurePanel>
|
|
||||||
</Disclosure>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
methods: {
|
|
||||||
setTheme(theme) {
|
|
||||||
if (theme === 'light') {
|
|
||||||
document.documentElement.classList.remove('dark');
|
|
||||||
} else if (theme === 'dark') {
|
|
||||||
document.documentElement.classList.add('dark');
|
|
||||||
} else if (theme === 'system') {
|
|
||||||
document.documentElement.classList.remove('dark');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { Disclosure, DisclosureButton, DisclosurePanel, Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/vue'
|
|
||||||
import { Bars3Icon, BellIcon, XMarkIcon } from '@heroicons/vue/24/outline'
|
|
||||||
|
|
||||||
const navigation = [
|
|
||||||
{ name: 'Forum', href: 'https://forum.beammp.com/', current: false },
|
|
||||||
{ name: 'Docs', href: 'https://docs.beammp.com/', current: false },
|
|
||||||
{ name: 'Stats', href: '/stats', current: false },
|
|
||||||
{ name: 'Servers', href: '/servers', current: false },
|
|
||||||
{ name: 'Hosting', href: '/hosting', current: false },
|
|
||||||
{ name: 'Events', href: 'https://forum.beammp.com/c/important/events/25', current: false },
|
|
||||||
{ name: 'Store', href: 'https://store.beammp.com/', current: false },
|
|
||||||
{ name: 'GitHub', href: 'https://github.com/BeamMP', current: false },
|
|
||||||
{ name: 'Patreon', href: 'https://patreon.com/BeamMP', current: false },
|
|
||||||
]
|
|
||||||
|
|
||||||
var isOpen = false
|
|
||||||
</script>
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="bg-white py-24 sm:py-32">
|
|
||||||
<div class="mx-auto max-w-7xl px-6 lg:px-8">
|
|
||||||
<h2 class="text-center text-lg font-semibold leading-8 text-gray-900">Used by some of the largest and most innovative BeamNG communities</h2>
|
|
||||||
<div class="mx-auto mt-10 grid max-w-lg grid-cols-4 items-center gap-x-8 gap-y-10 sm:max-w-xl sm:grid-cols-6 sm:gap-x-10 lg:mx-0 lg:max-w-none lg:grid-cols-5">
|
|
||||||
<a href="https://discord.gg/carp"><img class="col-span-2 max-h-10 w-full object-contain lg:col-span-1" src="@/assets/communities/carp-logo.png" alt="CaRP" width="158" height="48" /></a>
|
|
||||||
<a href="https://discord.2fast.racing/"><img class="col-span-2 max-h-12 w-full object-contain lg:col-span-1" src="@/assets/communities/2fast-logo.png" alt="2FastRacing" width="158" height="48" /></a>
|
|
||||||
<a href="https://discord.beamcruise.com"><img class="col-span-2 max-h-12 w-full object-contain lg:col-span-1" src="@/assets/communities/beamcruise-logo.png" alt="Beam\\Cruise" width="158" height="48" /></a>
|
|
||||||
<a href="https://discord.gg/b9G3fXKqpg"><img class="col-span-2 max-h-12 w-full object-contain lg:col-span-1" src="@/assets/communities/nuclear-logo.png" alt="CnR" width="158" height="48" /></a>
|
|
||||||
<img class="col-span-2 col-start-2 max-h-12 w-full object-contain sm:col-start-auto lg:col-span-1" src="@/assets/beammp-logo.png" alt="Community E" width="158" height="48" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="bg-white">
|
|
||||||
<div class="mx-auto max-w-7xl py-24 sm:px-6 sm:py-32 lg:px-8">
|
|
||||||
<div class="relative isolate overflow-hidden bg-gray-900 px-6 pt-16 shadow-2xl sm:rounded-3xl sm:px-16 md:pt-24 lg:flex lg:gap-x-20 lg:px-24 lg:pt-0">
|
|
||||||
<svg viewBox="0 0 1024 1024" class="absolute left-1/2 top-1/2 -z-10 h-[64rem] w-[64rem] -translate-y-1/2 [mask-image:radial-gradient(closest-side,white,transparent)] sm:left-full sm:-ml-80 lg:left-1/2 lg:ml-0 lg:-translate-x-1/2 lg:translate-y-0" aria-hidden="true">
|
|
||||||
<circle cx="512" cy="512" r="512" fill="url(#759c1415-0410-454c-8f7c-9a820de03641)" fill-opacity="0.7" />
|
|
||||||
<defs>
|
|
||||||
<radialGradient id="759c1415-0410-454c-8f7c-9a820de03641">
|
|
||||||
<stop stop-color="#7775D6" />
|
|
||||||
<stop offset="1" stop-color="#E935C1" />
|
|
||||||
</radialGradient>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
||||||
<div class="mx-auto max-w-md text-center lg:mx-0 lg:flex-auto lg:py-32 lg:text-left">
|
|
||||||
<h2 class="text-3xl font-bold tracking-tight text-white sm:text-4xl">Looking to write your own mods, plugins or resources?</h2>
|
|
||||||
<p class="mt-6 text-lg leading-8 text-gray-300">Check out our docs to get you started with developing your own mods and resources.</p>
|
|
||||||
<div class="mt-10 flex items-center justify-center gap-x-6 lg:justify-start">
|
|
||||||
<a href="https://docs.beammp.com" class="rounded-md bg-white px-3.5 py-2.5 text-sm font-semibold text-gray-900 shadow-sm hover:bg-gray-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white">Learn more <span aria-hidden="true">→</span></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="relative mt-16 h-80 lg:mt-8">
|
|
||||||
<img class="absolute left-0 top-0 w-[57rem] max-w-none rounded-md bg-white/5 ring-1 ring-white/10" src="https://tailwindui.com/img/component-images/dark-project-app-screenshot.png" alt="App screenshot" width="1824" height="1080" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="overflow-hidden bg-white py-24 sm:py-32">
|
|
||||||
<div class="mx-auto max-w-7xl px-6 lg:px-8">
|
|
||||||
<div class="mx-auto grid max-w-2xl grid-cols-1 gap-x-8 gap-y-16 sm:gap-y-20 lg:mx-0 lg:max-w-none lg:grid-cols-2">
|
|
||||||
<div class="lg:pr-8 lg:pt-4">
|
|
||||||
<div class="lg:max-w-lg">
|
|
||||||
<h2 class="text-base font-semibold leading-7 text-indigo-600">Integrated Server list</h2>
|
|
||||||
<p class="mt-2 text-3xl font-bold tracking-tight text-gray-900 sm:text-4xl">Choose a server to your liking</p>
|
|
||||||
<p class="mt-6 text-lg leading-8 text-gray-600">BeamMP has a builtin server list allowing you to select the server that appeals to your liking the most.</p>
|
|
||||||
<dl class="mt-10 max-w-xl space-y-8 text-base leading-7 text-gray-600 lg:max-w-none">
|
|
||||||
<div v-for="feature in features" :key="feature.name" class="relative pl-9">
|
|
||||||
<dt class="inline font-semibold text-gray-900">
|
|
||||||
<component :is="feature.icon" class="absolute left-1 top-1 h-5 w-5 text-indigo-600" aria-hidden="true" />
|
|
||||||
{{ feature.name }}
|
|
||||||
</dt>
|
|
||||||
{{ ' ' }}
|
|
||||||
<dd class="inline">{{ feature.description }}</dd>
|
|
||||||
</div>
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<img src="@/assets/beammp-servers.png" alt="Product screenshot" class="w-[48rem] max-w-none rounded-xl shadow-xl ring-1 ring-gray-400/10 sm:w-[57rem] md:-ml-4 lg:-ml-0" width="2432" height="1442" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { CloudArrowUpIcon, LockClosedIcon, ServerIcon } from '@heroicons/vue/20/solid'
|
|
||||||
|
|
||||||
const features = [
|
|
||||||
{
|
|
||||||
name: 'Custom mods',
|
|
||||||
description: 'BeamMP includes resource sharing functionality so that you can share your favorite mods with your friends directly from the same server. This applies with each and every server too!',
|
|
||||||
icon: CloudArrowUpIcon,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Public & Private Servers',
|
|
||||||
description: 'You have full control over your server including if you want it public for others to join or not.',
|
|
||||||
icon: LockClosedIcon,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Self and Paid Hosting',
|
|
||||||
description: 'You are free to host your own server or check out one of our parters who can have you up and running in minutes.',
|
|
||||||
icon: ServerIcon,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
</script>
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="overflow-hidden bg-white py-24 sm:py-32">
|
|
||||||
<div class="mx-auto max-w-7xl px-6 lg:px-8">
|
|
||||||
<div class="mx-auto grid max-w-2xl grid-cols-1 gap-x-8 gap-y-16 sm:gap-y-20 lg:mx-0 lg:max-w-none lg:grid-cols-2">
|
|
||||||
<div class="lg:pr-8 lg:pt-4">
|
|
||||||
<div class="lg:max-w-lg">
|
|
||||||
<h2 class="text-base font-semibold leading-7 text-indigo-600">Integrated Server list</h2>
|
|
||||||
<p class="mt-2 text-3xl font-bold tracking-tight text-gray-900 sm:text-4xl">Choose a server to your liking</p>
|
|
||||||
<p class="mt-6 text-lg leading-8 text-gray-600">BeamMP has a builtin server list allowing you to select the server that appeals to your liking the most.</p>
|
|
||||||
<dl class="mt-10 max-w-xl space-y-8 text-base leading-7 text-gray-600 lg:max-w-none">
|
|
||||||
<div v-for="feature in features" :key="feature.name" class="relative pl-9">
|
|
||||||
<dt class="inline font-semibold text-gray-900">
|
|
||||||
<component :is="feature.icon" class="absolute left-1 top-1 h-5 w-5 text-indigo-600" aria-hidden="true" />
|
|
||||||
{{ feature.name }}
|
|
||||||
</dt>
|
|
||||||
{{ ' ' }}
|
|
||||||
<dd class="inline">{{ feature.description }}</dd>
|
|
||||||
</div>
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<img src="@/assets/beammp-servers.png" alt="Product screenshot" class="w-[48rem] max-w-none rounded-xl shadow-xl ring-1 ring-gray-400/10 sm:w-[57rem] md:-ml-4 lg:-ml-0" width="2432" height="1442" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { CloudArrowUpIcon, LockClosedIcon, ServerIcon } from '@heroicons/vue/20/solid'
|
|
||||||
|
|
||||||
const features = [
|
|
||||||
{
|
|
||||||
name: 'Custom mods',
|
|
||||||
description: 'BeamMP includes resource sharing functionality so that you can share your favorite mods with your friends directly from the same server. This applies with each and every server too!',
|
|
||||||
icon: CloudArrowUpIcon,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Public & Private Servers',
|
|
||||||
description: 'You have full control over your server including if you want it public for others to join or not.',
|
|
||||||
icon: LockClosedIcon,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Self and Paid Hosting',
|
|
||||||
description: 'You are free to host your own server or check out one of our parters who can have you up and running in minutes.',
|
|
||||||
icon: ServerIcon,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
</script>
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
<template>
|
|
||||||
<header class="main-content" :style="{ 'background-position': 'center center, center 0px' }">
|
|
||||||
<div class="introduction" style="margin-top: 0px;">
|
|
||||||
<div class="container mx-auto">
|
|
||||||
<div class="flex items-center justify-center">
|
|
||||||
<div class="lg:w-8/12 md:w-8/12 sm:w-8/12 xs:w-full">
|
|
||||||
<p class="lead">
|
|
||||||
<b>BeamMP</b> Bringing Multiplayer to BeamNG.drive!<br>
|
|
||||||
With a smooth and enjoyable experience.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div id="button" class="lg:w-4/12 md:w-4/12 sm:w-4/12 xs:w-full hidden-xs buttons-wrapper">
|
|
||||||
<div class="buttons mt-3">
|
|
||||||
<button @click="downloadInstaller" class="download-client js-show-story" href="beamMP.zip" download="">
|
|
||||||
<img class="os-icon" src="https://raw.githubusercontent.com/devicons/devicon/master/icons/windows8/windows8-original.svg" alt="windows-logo">
|
|
||||||
<span class="text">Download Client</span>
|
|
||||||
<span class="description"> BeamMP_Installer.zip</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="buttons mt-3">
|
|
||||||
<form action="https://github.com/BeamMP/BeamMP-Server/releases/latest/download/BeamMP-Server.exe">
|
|
||||||
<button class="download-client js-show-story" style="background: radial-gradient(circle,rgb(255 179 38 / 80%),rgb(255 176 0 / 65%)) center/100%;">
|
|
||||||
<img class="os-icon" src="https://raw.githubusercontent.com/devicons/devicon/master/icons/windows8/windows8-original.svg" alt="windows-logo">
|
|
||||||
<span class="text">Download Server</span>
|
|
||||||
<span class="description"> BeamMP-Server.exe</span>
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div class="buttons mt-3">
|
|
||||||
<form action="https://github.com/BeamMP/BeamMP-Server/releases/latest">
|
|
||||||
<button class="download-client js-show-story" style="background: radial-gradient(circle,rgb(255 179 38 / 80%),rgb(255 176 0 / 65%)) center/100%;">
|
|
||||||
<img class="os-icon" src="https://raw.githubusercontent.com/devicons/devicon/master/icons/linux/linux-plain.svg" alt="linux-logo">
|
|
||||||
<span class="text">Download Server</span>
|
|
||||||
<span class="description">Linux builds</span>
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
methods: {
|
|
||||||
downloadInstaller() {
|
|
||||||
// Implement your download logic here
|
|
||||||
// You can use the window.location.href or any other method
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
/* Add your Tailwind CSS styles here */
|
|
||||||
</style>
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="bg-white py-24 sm:py-32">
|
|
||||||
<div class="mx-auto max-w-7xl px-6 lg:px-8">
|
|
||||||
<h2 class="text-center text-lg font-semibold leading-8 text-gray-900">Proudly partnered with trusted hosting companies</h2>
|
|
||||||
<div v-for="partner in partners" :key="partner.id" class="mx-auto mt-10 grid max-w-lg grid-cols-4 items-center gap-x-8 gap-y-10 sm:max-w-xl sm:grid-cols-6 sm:gap-x-10 lg:mx-0 lg:max-w-none lg:grid-cols-5">
|
|
||||||
<a :href="partner.link"><img class="col-span-2 max-h-12 w-full object-contain lg:col-span-1" :src="partner.logo" :alt="partner.alt" width="158" height="48" /></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
const partners = [
|
|
||||||
{ id: 1, logo: 'src/assets/partners/horizon_hosting-logo.png', alt: 'Horizon Hosting', link: 'https://billing.horizonnetworks.uk/store/beammp-server-hosting' },
|
|
||||||
]
|
|
||||||
</script>
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="bg-white dark:bg-slate-800 py-24 sm:py-32">
|
|
||||||
<div class="mx-auto max-w-7xl px-6 lg:px-8">
|
|
||||||
<dl class="grid grid-cols-1 gap-x-8 gap-y-16 text-center lg:grid-cols-4">
|
|
||||||
<div v-for="stat in stats" :key="stat.id" class="mx-auto flex max-w-xs flex-col gap-y-4">
|
|
||||||
<a :href="stat.link"><dt class="text-base leading-7 text-gray-600 dark:text-white">{{ stat.name }}</dt></a>
|
|
||||||
<dd class="order-first text-3xl font-semibold tracking-tight text-gray-900 sm:text-5xl dark:text-slate-400">{{ stat.value }}</dd>
|
|
||||||
</div>
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
const stats = [
|
|
||||||
{ id: 1, name: 'Players Online', value: '1500', link: '/stats' },
|
|
||||||
{ id: 2, name: 'Public Servers', value: '1200', link: '/stats' },
|
|
||||||
{ id: 3, name: 'Users', value: '+760,000', link: 'https://forum.beammp.com' },
|
|
||||||
{ id: 4, name: 'Discord Members', value: '+190,000', link: 'https://discord.com/servers/beammp-601558901657305098' },
|
|
||||||
]
|
|
||||||
</script>
|
|
||||||
@@ -1,169 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="expanded-row-details">
|
|
||||||
<h1 style="padding-left:10px;display:flex;">
|
|
||||||
<template v-if="rowData.raw.official">
|
|
||||||
<img src="/src/assets/beammp-logo.png" alt="" style="height: 23px; padding-right: 10px;"> [Official Server]
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<img src="/src/assets/beammp-logo.png" alt="" style="height: 23px; padding-right: 10px;">
|
|
||||||
</template>
|
|
||||||
<span v-for="(value, name) in rowData.name" :style="value.f">{{ value.s }}</span>
|
|
||||||
</h1>
|
|
||||||
<div class="columns-2">
|
|
||||||
<div>
|
|
||||||
<h2 class="text-xl">Information:</h2>
|
|
||||||
<hr/>
|
|
||||||
<table class="description-table">
|
|
||||||
<tr>
|
|
||||||
<td>Owner:</td>
|
|
||||||
<td>{{rowData.raw.owner|| ""}}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Map:</td>
|
|
||||||
<td>{{rowData.map || ""}}</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div>
|
|
||||||
<h2 class="text-xl">Description:</h2>
|
|
||||||
<hr/>
|
|
||||||
<p><span v-for="(value, name) in formatDescriptionName(rowData.raw.sdesc)" :style="value.f">{{ value.s }}</span></p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h2 class="text-xl">Mods ({{modCount(rowData.raw.modlist|| "")}}):</h2>
|
|
||||||
<hr/>
|
|
||||||
<p>{{modList(rowData.raw.modlist|| "")}} ({{formatBytes(rowData.raw.modstotalsize) || "0B"}})</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<h1 class="text-xl">Players ({{rowData.players|| "0"}}):</h1>
|
|
||||||
<hr/>
|
|
||||||
<p>{{listPlayers(rowData.raw.playerslist|| "")}}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
var descStyleMap = {
|
|
||||||
'^0': 'color:#000000',
|
|
||||||
'^1': 'color:#0000AA',
|
|
||||||
'^2': 'color:#00AA00',
|
|
||||||
'^3': 'color:#00AAAA',
|
|
||||||
'^4': 'color:#AA0000',
|
|
||||||
'^5': 'color:#AA00AA',
|
|
||||||
'^6': 'color:#FFAA00',
|
|
||||||
'^7': 'color:#AAAAAA',
|
|
||||||
'^8': 'color:#555555',
|
|
||||||
'^9': 'color:#5555FF',
|
|
||||||
'^a': 'color:#55FF55',
|
|
||||||
'^b': 'color:#55FFFF',
|
|
||||||
'^c': 'color:#FF5555',
|
|
||||||
'^d': 'color:#FF55FF',
|
|
||||||
'^e': 'color:#FFFF55',
|
|
||||||
'^f': 'color:#FFFFFF',
|
|
||||||
'^l': 'font-weight:bold',
|
|
||||||
'^m': 'text-decoration:line-through',
|
|
||||||
'^n': 'text-decoration:underline',
|
|
||||||
'^o': 'font-style:italic',
|
|
||||||
};
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
rowData: {
|
|
||||||
type: Object,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
modCount(s) {
|
|
||||||
if(s.length==0) return 0;
|
|
||||||
return s.split(";").length-1;
|
|
||||||
},
|
|
||||||
modList(s) {
|
|
||||||
var modarray = s.split(';');
|
|
||||||
//console.log(modarray);
|
|
||||||
s = "";
|
|
||||||
|
|
||||||
for (var i=0; i<modarray.length-1; i++){
|
|
||||||
var modName = modarray[i].split('/').pop();
|
|
||||||
modName = modName.replace(".zip","");
|
|
||||||
s += modName;
|
|
||||||
//if (i<modarray.length-2)
|
|
||||||
s += ", ";
|
|
||||||
}
|
|
||||||
//console.log(s);
|
|
||||||
s = s.substring(0, s.length -2);
|
|
||||||
return s
|
|
||||||
},
|
|
||||||
formatBytes(bytes = 0, decimals = 2) {
|
|
||||||
if (bytes == 0 || bytes == undefined) return '0 Bytes';
|
|
||||||
const k = 1024;
|
|
||||||
const dm = decimals < 0 ? 0 : decimals;
|
|
||||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
|
||||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
||||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
|
|
||||||
},
|
|
||||||
listPlayers(s) {
|
|
||||||
if (s != undefined || s != "") {
|
|
||||||
var re = new RegExp(";", 'g');
|
|
||||||
s = s.replace(re, ', ');
|
|
||||||
s = s.substring(0, s.length -2);
|
|
||||||
return s
|
|
||||||
} else {
|
|
||||||
return "No players..."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
applyCode(string, codes) {
|
|
||||||
var cssText = ''
|
|
||||||
string = string.replace(/\x00*/g, '');
|
|
||||||
for (var i = 0, len = codes.length; i < len; i++) {
|
|
||||||
cssText += styleMap[codes[i]] + ';';
|
|
||||||
}
|
|
||||||
return {s: string, f:cssText};
|
|
||||||
},
|
|
||||||
formatDescriptionName(string) {
|
|
||||||
var codes = string.match(/\^.{1}/g) || [],
|
|
||||||
indexes = [],
|
|
||||||
apply = [],
|
|
||||||
tmpStr,
|
|
||||||
name = [],
|
|
||||||
i,
|
|
||||||
len;
|
|
||||||
for (i = 0, len = codes.length; i < len; i++) {
|
|
||||||
indexes.push(string.indexOf(codes[i]));
|
|
||||||
string = string.replace(codes[i], '\x00\x00');
|
|
||||||
}
|
|
||||||
if (indexes[0] !== 0) {
|
|
||||||
name.push(this.applyCode(string.substring(0, indexes[0]), []))
|
|
||||||
}
|
|
||||||
for (i = 0; i < len; i++) {
|
|
||||||
var indexDelta = indexes[i + 1] - indexes[i];
|
|
||||||
if (indexDelta === 2) {
|
|
||||||
while (indexDelta === 2) {
|
|
||||||
apply.push(codes[i]);
|
|
||||||
i++;
|
|
||||||
indexDelta = indexes[i + 1] - indexes[i];
|
|
||||||
}
|
|
||||||
apply.push(codes[i]);
|
|
||||||
} else {
|
|
||||||
apply.push(codes[i]);
|
|
||||||
}
|
|
||||||
if (apply.lastIndexOf('^r') > -1) {
|
|
||||||
apply = apply.slice(apply.lastIndexOf('^r') + 1);
|
|
||||||
}
|
|
||||||
tmpStr = string.substring(indexes[i], indexes[i + 1]);
|
|
||||||
name.push(this.applyCode(tmpStr, apply))
|
|
||||||
}
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>/* Add styles for the expanded row details */
|
|
||||||
.expanded-row-details {
|
|
||||||
padding: 10px;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
}</style>
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="table-container">
|
|
||||||
<table class="min-w-full divide-y divide-gray-400">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th v-for="column in columns" :key="column.key" class="px-6 py-3 bg-gray-200 text-left text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
{{ column.label }}
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<template v-for="item in data" :key="item.id">
|
|
||||||
<tr @click="selectRow(item.id)" class="servers-table-row">
|
|
||||||
<td v-for="column in columns" :key="column.key" class="px-6 py-1 whitespace-no-wrap text-sm leading-5 text-gray-600" :style="item.style" >
|
|
||||||
<template v-if="column.key == 'location'">
|
|
||||||
<span style="display:flex;width: 125px;">
|
|
||||||
<img :src="`/src/assets/flags/${item.cc}.png`" alt="" style="padding-right: 10px;">
|
|
||||||
<span style="position: absolute;left: 105px;">{{ item[column.key] }}</span>
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
<template v-else-if="column.key == 'name'">
|
|
||||||
<span v-for="(value, name) in item[column.key]" :style="value.f">{{ value.s }}</span>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
{{ item[column.key] }}
|
|
||||||
</template>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr v-if="selectedRow == item.id">
|
|
||||||
<td :colspan="columns.length">
|
|
||||||
<ExpandedRowDetails :rowData="getExpandedRowData(item)" />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import ExpandedRowDetails from "@/components/servers/ExpandedRowDetails.vue";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
ExpandedRowDetails,
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
columns: {
|
|
||||||
type: Array,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
type: Array,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
selectedRow: null,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
selectRow(id) {
|
|
||||||
if (this.selectedRow === id) {
|
|
||||||
this.selectedRow = null;
|
|
||||||
} else {
|
|
||||||
this.selectedRow = id;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getExpandedRowData(item) {
|
|
||||||
return item || {}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.table-container {
|
|
||||||
overflow-x: auto;
|
|
||||||
}
|
|
||||||
.servers-table-row:hover {
|
|
||||||
background-color: rgba(0, 0, 0, 0.25);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
<!-- https://github.com/VanOord/vue3-plotly/pull/10/files -->
|
|
||||||
<template>
|
|
||||||
<div :id="plotlyId"></div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
let Plotly;
|
|
||||||
import events from "./events.js";
|
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
|
||||||
|
|
||||||
let timeOutFunctionId;
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'VuePlotly',
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
plotlyId: `plotly-${uuidv4()}`,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
props: {
|
|
||||||
'data' : {
|
|
||||||
type : Array,
|
|
||||||
required:false,
|
|
||||||
},
|
|
||||||
'layout': {
|
|
||||||
type : Object,
|
|
||||||
required:false,
|
|
||||||
},
|
|
||||||
'config':{
|
|
||||||
type : Object,
|
|
||||||
required:false,
|
|
||||||
},
|
|
||||||
'bundle':{
|
|
||||||
type : String,
|
|
||||||
default : "full",
|
|
||||||
required:false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
data() { this.setGraph(); },
|
|
||||||
layout() { this.setGraph(); },
|
|
||||||
config() { this.setGraph(); },
|
|
||||||
},
|
|
||||||
|
|
||||||
async mounted() {
|
|
||||||
switch(this.bundle){
|
|
||||||
//case "basic" : Plotly = await import("plotly.js-basic-dist-min"); break;
|
|
||||||
//case "cartesian" : Plotly = await import("plotly.js-cartesian-dist-min"); break;
|
|
||||||
//case "geo" : Plotly = await import("plotly.js-geo-dist-min"); break;
|
|
||||||
//case "gl3d" : Plotly = await import("plotly.js-gl3d-dist-min"); break;
|
|
||||||
//case "gl2d" : Plotly = await import("plotly.js-gl2d-dist-min"); break;
|
|
||||||
//case "mapbox" : Plotly = await import("plotly.js-mapbox-dist-min"); break;
|
|
||||||
//case "finance" : Plotly = await import("plotly.js-finance-dist-min"); break;
|
|
||||||
//case "strict" : Plotly = await import("plotly.js-strict-dist-min"); break;
|
|
||||||
default : Plotly = await import("plotly.js-dist");
|
|
||||||
}
|
|
||||||
this.setGraph();
|
|
||||||
events.forEach(evt => {
|
|
||||||
this.$el.on(evt.completeName, evt.handler(this));
|
|
||||||
});
|
|
||||||
this.resizeObserver = new ResizeObserver(() => {
|
|
||||||
clearTimeout(timeOutFunctionId); // debounce the reset
|
|
||||||
timeOutFunctionId = setTimeout(this.setGraph, 100);
|
|
||||||
});
|
|
||||||
this.resizeObserver.observe(document.getElementById(this.plotlyId));
|
|
||||||
},
|
|
||||||
|
|
||||||
beforeUnmount() {
|
|
||||||
events.forEach(event => this.$el.removeAllListeners(event.completeName));
|
|
||||||
this.resizeObserver.disconnect();
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
setGraph() {
|
|
||||||
Plotly.newPlot(this.plotlyId, this.data, this.layout, this.config);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
const eventsName = [
|
|
||||||
"AfterExport",
|
|
||||||
"AfterPlot",
|
|
||||||
"Animated",
|
|
||||||
"AnimatingFrame",
|
|
||||||
"AnimationInterrupted",
|
|
||||||
"AutoSize",
|
|
||||||
"BeforeExport",
|
|
||||||
"ButtonClicked",
|
|
||||||
"Click",
|
|
||||||
"ClickAnnotation",
|
|
||||||
"Deselect",
|
|
||||||
"DoubleClick",
|
|
||||||
"Framework",
|
|
||||||
"Hover",
|
|
||||||
"LegendClick",
|
|
||||||
"LegendDoubleClick",
|
|
||||||
"Relayout",
|
|
||||||
"Restyle",
|
|
||||||
"Redraw",
|
|
||||||
"Selected",
|
|
||||||
"Selecting",
|
|
||||||
"SliderChange",
|
|
||||||
"SliderEnd",
|
|
||||||
"SliderStart",
|
|
||||||
"Transitioning",
|
|
||||||
"TransitionInterrupted",
|
|
||||||
"Unhover"
|
|
||||||
];
|
|
||||||
|
|
||||||
const events = eventsName
|
|
||||||
.map(evt => evt.toLocaleLowerCase())
|
|
||||||
.map(eventName => ({
|
|
||||||
completeName: "plotly_" + eventName,
|
|
||||||
handler: context => (...args) => {
|
|
||||||
context.$emit.apply(context, [eventName, ...args]);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
export default events;
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import { createApp } from 'vue'
|
|
||||||
import './style.css'
|
|
||||||
import App from './App.vue'
|
|
||||||
import router from './router'
|
|
||||||
|
|
||||||
createApp(App).use(router).mount('#app')
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import { createRouter, createWebHistory } from 'vue-router'
|
|
||||||
import Home from '@/views/Home.vue'
|
|
||||||
|
|
||||||
export default createRouter({
|
|
||||||
history: createWebHistory(),
|
|
||||||
routes: [
|
|
||||||
{
|
|
||||||
path: '/',
|
|
||||||
component: Home,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/stats',
|
|
||||||
component: () => import('@/views/Stats.vue'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/servers',
|
|
||||||
component: () => import('@/views/Servers.vue'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/hosting',
|
|
||||||
component: () => import('@/views/Hosting.vue'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/rules',
|
|
||||||
component: () => import('@/views/Rules.vue'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/privacy',
|
|
||||||
component: () => import('@/views/Privacy.vue'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/terms',
|
|
||||||
component: () => import('@/views/Terms.vue'),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
@@ -1,118 +0,0 @@
|
|||||||
@tailwind base;
|
|
||||||
@tailwind components;
|
|
||||||
@tailwind utilities;
|
|
||||||
|
|
||||||
.main-content {
|
|
||||||
display: flex;
|
|
||||||
position: relative;
|
|
||||||
-webkit-box-align: center;
|
|
||||||
-ms-flex-align: center;
|
|
||||||
align-items: center;
|
|
||||||
width: 100%;
|
|
||||||
height: calc(100vh - 80px);
|
|
||||||
overflow: hidden;
|
|
||||||
background: linear-gradient(-45deg, rgba(22, 25, 35, 0.35) 0%, rgba(22, 25, 35, 0.95) 100%) center center/100%, url(./assets/beamng-mp-landing.png) center top/cover;
|
|
||||||
color: #fefee1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-content .introduction {
|
|
||||||
width: 100%;
|
|
||||||
position: absolute;
|
|
||||||
transition: transform .2s ease;
|
|
||||||
transform: translateX(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-content .lead {
|
|
||||||
font-size: 30px;
|
|
||||||
font-weight: 300;
|
|
||||||
font-family: verdana;
|
|
||||||
}
|
|
||||||
|
|
||||||
.middle-xs {
|
|
||||||
-webkit-box-align: center;
|
|
||||||
-ms-flex-align: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.center-xs {
|
|
||||||
-webkit-box-pack: center;
|
|
||||||
-ms-flex-pack: center;
|
|
||||||
justify-content: center;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-content .buttons-wrapper {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-end;
|
|
||||||
align-content: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-content .buttons-wrapper .buttons .download-client {
|
|
||||||
display: block;
|
|
||||||
position: relative;
|
|
||||||
padding: 15px 25px 15px 25px;
|
|
||||||
cursor: pointer;
|
|
||||||
color: #fefee1;
|
|
||||||
border: none;
|
|
||||||
text-shadow: 0 0 1px rgba(22, 25, 35, 0.5);
|
|
||||||
background: radial-gradient(circle, rgba(150, 204, 0, 0.8), rgba(150, 204, 0, 0.65)) center/100%;
|
|
||||||
transition: box-shadow .2s ease;
|
|
||||||
font-size: 27px;
|
|
||||||
font-weight: 300;
|
|
||||||
line-height: 1;
|
|
||||||
border-radius: 3px;
|
|
||||||
text-decoration: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-moz-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
min-width: 297px;
|
|
||||||
padding-left: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.description {
|
|
||||||
display: block;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 200;
|
|
||||||
opacity: .9;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.buttons {
|
|
||||||
min-width: 297px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.buttons.mt-3 {
|
|
||||||
min-width: 297px;
|
|
||||||
}
|
|
||||||
|
|
||||||
img.os-icon {
|
|
||||||
width: 32px !important;
|
|
||||||
height: 32px !important;
|
|
||||||
position: absolute;
|
|
||||||
top: 18px;
|
|
||||||
left: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 600px) {
|
|
||||||
#button {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 767px) {
|
|
||||||
.text {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo-link {
|
|
||||||
display: flex;
|
|
||||||
height: 100%;
|
|
||||||
align-items: center;
|
|
||||||
align-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo-text {
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
<script setup>
|
|
||||||
import Hero from '@/components/home/Hero.vue'
|
|
||||||
import Stats from '@/components/home/Stats.vue'
|
|
||||||
import Communities from '@/components/home/Communities.vue'
|
|
||||||
import Partners from '@/components/home/Partners.vue'
|
|
||||||
import Featured2 from '@/components/home/Featured2.vue'
|
|
||||||
|
|
||||||
import Docs from '@/components/home/Docs.vue'
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<Hero />
|
|
||||||
<Stats />
|
|
||||||
<Communities />
|
|
||||||
<Featured2 />
|
|
||||||
<Partners />
|
|
||||||
<!-- Coders Portion -->
|
|
||||||
<Docs />
|
|
||||||
</template>
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
<template>
|
|
||||||
<h1>BeamMP Privacy Policy</h1>
|
|
||||||
</template>
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
<template>
|
|
||||||
<h1>BeamMP Privacy Policy</h1>
|
|
||||||
</template>
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
<template>
|
|
||||||
<h1>BeamMP Rules</h1>
|
|
||||||
</template>
|
|
||||||
@@ -1,440 +0,0 @@
|
|||||||
<script setup>
|
|
||||||
import Table from '@/components/servers/Table.vue'
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div id="app" class="p-8">
|
|
||||||
<Table :columns="columns" :data="tableData" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import axios from 'axios'
|
|
||||||
|
|
||||||
var styleMap = {
|
|
||||||
'^0': 'color:#000000',
|
|
||||||
'^1': 'color:#0000AA',
|
|
||||||
'^2': 'color:#00AA00',
|
|
||||||
'^3': 'color:#00AAAA',
|
|
||||||
'^4': 'color:#AA0000',
|
|
||||||
'^5': 'color:#AA00AA',
|
|
||||||
'^6': 'color:#FFAA00',
|
|
||||||
'^7': 'color:#AAAAAA',
|
|
||||||
'^8': 'color:#555555',
|
|
||||||
'^9': 'color:#5555FF',
|
|
||||||
'^a': 'color:#55FF55',
|
|
||||||
'^b': 'color:#55FFFF',
|
|
||||||
'^c': 'color:#FF5555',
|
|
||||||
'^d': 'color:#FF55FF',
|
|
||||||
'^e': 'color:#FFFF55',
|
|
||||||
'^f': 'color:#FFFFFF',
|
|
||||||
'^l': 'font-weight:bold',
|
|
||||||
'^m': 'text-decoration:line-through',
|
|
||||||
'^n': 'text-decoration:underline',
|
|
||||||
'^o': 'font-style:italic',
|
|
||||||
};
|
|
||||||
|
|
||||||
const isoCountries = {
|
|
||||||
'AF': 'Afghanistan',
|
|
||||||
'AX': 'Aland Islands',
|
|
||||||
'AL': 'Albania',
|
|
||||||
'DZ': 'Algeria',
|
|
||||||
'AS': 'American Samoa',
|
|
||||||
'AD': 'Andorra',
|
|
||||||
'AO': 'Angola',
|
|
||||||
'AI': 'Anguilla',
|
|
||||||
'AQ': 'Antarctica',
|
|
||||||
'AG': 'Antigua And Barbuda',
|
|
||||||
'AR': 'Argentina',
|
|
||||||
'AM': 'Armenia',
|
|
||||||
'AW': 'Aruba',
|
|
||||||
'AU': 'Australia',
|
|
||||||
'AT': 'Austria',
|
|
||||||
'AZ': 'Azerbaijan',
|
|
||||||
'BS': 'Bahamas',
|
|
||||||
'BH': 'Bahrain',
|
|
||||||
'BD': 'Bangladesh',
|
|
||||||
'BB': 'Barbados',
|
|
||||||
'BY': 'Belarus',
|
|
||||||
'BE': 'Belgium',
|
|
||||||
'BZ': 'Belize',
|
|
||||||
'BJ': 'Benin',
|
|
||||||
'BM': 'Bermuda',
|
|
||||||
'BT': 'Bhutan',
|
|
||||||
'BO': 'Bolivia',
|
|
||||||
'BA': 'Bosnia And Herzegovina',
|
|
||||||
'BW': 'Botswana',
|
|
||||||
'BV': 'Bouvet Island',
|
|
||||||
'BR': 'Brazil',
|
|
||||||
'IO': 'British Indian Ocean Territory',
|
|
||||||
'BN': 'Brunei Darussalam',
|
|
||||||
'BG': 'Bulgaria',
|
|
||||||
'BF': 'Burkina Faso',
|
|
||||||
'BI': 'Burundi',
|
|
||||||
'KH': 'Cambodia',
|
|
||||||
'CM': 'Cameroon',
|
|
||||||
'CA': 'Canada',
|
|
||||||
'CV': 'Cape Verde',
|
|
||||||
'KY': 'Cayman Islands',
|
|
||||||
'CF': 'Central African Republic',
|
|
||||||
'TD': 'Chad',
|
|
||||||
'CL': 'Chile',
|
|
||||||
'CN': 'China',
|
|
||||||
'CX': 'Christmas Island',
|
|
||||||
'CC': 'Cocos (Keeling) Islands',
|
|
||||||
'CO': 'Colombia',
|
|
||||||
'KM': 'Comoros',
|
|
||||||
'CG': 'Congo',
|
|
||||||
'CD': 'Congo, Democratic Republic',
|
|
||||||
'CK': 'Cook Islands',
|
|
||||||
'CR': 'Costa Rica',
|
|
||||||
'CI': 'Cote D\'Ivoire',
|
|
||||||
'HR': 'Croatia',
|
|
||||||
'CU': 'Cuba',
|
|
||||||
'CY': 'Cyprus',
|
|
||||||
'CZ': 'Czech Republic',
|
|
||||||
'DK': 'Denmark',
|
|
||||||
'DJ': 'Djibouti',
|
|
||||||
'DM': 'Dominica',
|
|
||||||
'DO': 'Dominican Republic',
|
|
||||||
'EC': 'Ecuador',
|
|
||||||
'EG': 'Egypt',
|
|
||||||
'SV': 'El Salvador',
|
|
||||||
'GQ': 'Equatorial Guinea',
|
|
||||||
'ER': 'Eritrea',
|
|
||||||
'EE': 'Estonia',
|
|
||||||
'ET': 'Ethiopia',
|
|
||||||
'FK': 'Falkland Islands (Malvinas)',
|
|
||||||
'FO': 'Faroe Islands',
|
|
||||||
'FJ': 'Fiji',
|
|
||||||
'FI': 'Finland',
|
|
||||||
'FR': 'France',
|
|
||||||
'GF': 'French Guiana',
|
|
||||||
'PF': 'French Polynesia',
|
|
||||||
'TF': 'French Southern Territories',
|
|
||||||
'GA': 'Gabon',
|
|
||||||
'GM': 'Gambia',
|
|
||||||
'GE': 'Georgia',
|
|
||||||
'DE': 'Germany',
|
|
||||||
'GH': 'Ghana',
|
|
||||||
'GI': 'Gibraltar',
|
|
||||||
'GR': 'Greece',
|
|
||||||
'GL': 'Greenland',
|
|
||||||
'GD': 'Grenada',
|
|
||||||
'GP': 'Guadeloupe',
|
|
||||||
'GU': 'Guam',
|
|
||||||
'GT': 'Guatemala',
|
|
||||||
'GG': 'Guernsey',
|
|
||||||
'GN': 'Guinea',
|
|
||||||
'GW': 'Guinea-Bissau',
|
|
||||||
'GY': 'Guyana',
|
|
||||||
'HT': 'Haiti',
|
|
||||||
'HM': 'Heard Island & Mcdonald Islands',
|
|
||||||
'VA': 'Holy See (Vatican City State)',
|
|
||||||
'HN': 'Honduras',
|
|
||||||
'HK': 'Hong Kong',
|
|
||||||
'HU': 'Hungary',
|
|
||||||
'IS': 'Iceland',
|
|
||||||
'IN': 'India',
|
|
||||||
'ID': 'Indonesia',
|
|
||||||
'IR': 'Iran, Islamic Republic Of',
|
|
||||||
'IQ': 'Iraq',
|
|
||||||
'IE': 'Ireland',
|
|
||||||
'IM': 'Isle Of Man',
|
|
||||||
'IL': 'Israel',
|
|
||||||
'IT': 'Italy',
|
|
||||||
'JM': 'Jamaica',
|
|
||||||
'JP': 'Japan',
|
|
||||||
'JE': 'Jersey',
|
|
||||||
'JO': 'Jordan',
|
|
||||||
'KZ': 'Kazakhstan',
|
|
||||||
'KE': 'Kenya',
|
|
||||||
'KI': 'Kiribati',
|
|
||||||
'KR': 'Korea',
|
|
||||||
'KW': 'Kuwait',
|
|
||||||
'KG': 'Kyrgyzstan',
|
|
||||||
'LA': 'Lao People\'s Democratic Republic',
|
|
||||||
'LV': 'Latvia',
|
|
||||||
'LB': 'Lebanon',
|
|
||||||
'LS': 'Lesotho',
|
|
||||||
'LR': 'Liberia',
|
|
||||||
'LY': 'Libyan Arab Jamahiriya',
|
|
||||||
'LI': 'Liechtenstein',
|
|
||||||
'LT': 'Lithuania',
|
|
||||||
'LU': 'Luxembourg',
|
|
||||||
'MO': 'Macao',
|
|
||||||
'MK': 'Macedonia',
|
|
||||||
'MG': 'Madagascar',
|
|
||||||
'MW': 'Malawi',
|
|
||||||
'MY': 'Malaysia',
|
|
||||||
'MV': 'Maldives',
|
|
||||||
'ML': 'Mali',
|
|
||||||
'MT': 'Malta',
|
|
||||||
'MH': 'Marshall Islands',
|
|
||||||
'MQ': 'Martinique',
|
|
||||||
'MR': 'Mauritania',
|
|
||||||
'MU': 'Mauritius',
|
|
||||||
'YT': 'Mayotte',
|
|
||||||
'MX': 'Mexico',
|
|
||||||
'FM': 'Micronesia, Federated States Of',
|
|
||||||
'MD': 'Moldova',
|
|
||||||
'MC': 'Monaco',
|
|
||||||
'MN': 'Mongolia',
|
|
||||||
'ME': 'Montenegro',
|
|
||||||
'MS': 'Montserrat',
|
|
||||||
'MA': 'Morocco',
|
|
||||||
'MZ': 'Mozambique',
|
|
||||||
'MM': 'Myanmar',
|
|
||||||
'NA': 'Namibia',
|
|
||||||
'NR': 'Nauru',
|
|
||||||
'NP': 'Nepal',
|
|
||||||
'NL': 'Netherlands',
|
|
||||||
'AN': 'Netherlands Antilles',
|
|
||||||
'NC': 'New Caledonia',
|
|
||||||
'NZ': 'New Zealand',
|
|
||||||
'NI': 'Nicaragua',
|
|
||||||
'NE': 'Niger',
|
|
||||||
'NG': 'Nigeria',
|
|
||||||
'NU': 'Niue',
|
|
||||||
'NF': 'Norfolk Island',
|
|
||||||
'MP': 'Northern Mariana Islands',
|
|
||||||
'NO': 'Norway',
|
|
||||||
'OM': 'Oman',
|
|
||||||
'PK': 'Pakistan',
|
|
||||||
'PW': 'Palau',
|
|
||||||
'PS': 'Palestinian Territory, Occupied',
|
|
||||||
'PA': 'Panama',
|
|
||||||
'PG': 'Papua New Guinea',
|
|
||||||
'PY': 'Paraguay',
|
|
||||||
'PE': 'Peru',
|
|
||||||
'PH': 'Philippines',
|
|
||||||
'PN': 'Pitcairn',
|
|
||||||
'PL': 'Poland',
|
|
||||||
'PT': 'Portugal',
|
|
||||||
'PR': 'Puerto Rico',
|
|
||||||
'QA': 'Qatar',
|
|
||||||
'RE': 'Reunion',
|
|
||||||
'RO': 'Romania',
|
|
||||||
'RU': 'Russian Federation',
|
|
||||||
'RW': 'Rwanda',
|
|
||||||
'BL': 'Saint Barthelemy',
|
|
||||||
'SH': 'Saint Helena',
|
|
||||||
'KN': 'Saint Kitts And Nevis',
|
|
||||||
'LC': 'Saint Lucia',
|
|
||||||
'MF': 'Saint Martin',
|
|
||||||
'PM': 'Saint Pierre And Miquelon',
|
|
||||||
'VC': 'Saint Vincent And Grenadines',
|
|
||||||
'WS': 'Samoa',
|
|
||||||
'SM': 'San Marino',
|
|
||||||
'ST': 'Sao Tome And Principe',
|
|
||||||
'SA': 'Saudi Arabia',
|
|
||||||
'SN': 'Senegal',
|
|
||||||
'RS': 'Serbia',
|
|
||||||
'SC': 'Seychelles',
|
|
||||||
'SL': 'Sierra Leone',
|
|
||||||
'SG': 'Singapore',
|
|
||||||
'SK': 'Slovakia',
|
|
||||||
'SI': 'Slovenia',
|
|
||||||
'SB': 'Solomon Islands',
|
|
||||||
'SO': 'Somalia',
|
|
||||||
'ZA': 'South Africa',
|
|
||||||
'GS': 'South Georgia And Sandwich Isl.',
|
|
||||||
'ES': 'Spain',
|
|
||||||
'LK': 'Sri Lanka',
|
|
||||||
'SD': 'Sudan',
|
|
||||||
'SR': 'Suriname',
|
|
||||||
'SJ': 'Svalbard And Jan Mayen',
|
|
||||||
'SZ': 'Swaziland',
|
|
||||||
'SE': 'Sweden',
|
|
||||||
'CH': 'Switzerland',
|
|
||||||
'SY': 'Syrian Arab Republic',
|
|
||||||
'TW': 'Taiwan',
|
|
||||||
'TJ': 'Tajikistan',
|
|
||||||
'TZ': 'Tanzania',
|
|
||||||
'TH': 'Thailand',
|
|
||||||
'TL': 'Timor-Leste',
|
|
||||||
'TG': 'Togo',
|
|
||||||
'TK': 'Tokelau',
|
|
||||||
'TO': 'Tonga',
|
|
||||||
'TT': 'Trinidad And Tobago',
|
|
||||||
'TN': 'Tunisia',
|
|
||||||
'TR': 'Turkey',
|
|
||||||
'TM': 'Turkmenistan',
|
|
||||||
'TC': 'Turks And Caicos Islands',
|
|
||||||
'TV': 'Tuvalu',
|
|
||||||
'UG': 'Uganda',
|
|
||||||
'UA': 'Ukraine',
|
|
||||||
'AE': 'United Arab Emirates',
|
|
||||||
'GB': 'United Kingdom',
|
|
||||||
'US': 'United States',
|
|
||||||
'UM': 'United States Outlying Islands',
|
|
||||||
'UY': 'Uruguay',
|
|
||||||
'UZ': 'Uzbekistan',
|
|
||||||
'VU': 'Vanuatu',
|
|
||||||
'VE': 'Venezuela',
|
|
||||||
'VN': 'Viet Nam',
|
|
||||||
'VG': 'Virgin Islands, British',
|
|
||||||
'VI': 'Virgin Islands, U.S.',
|
|
||||||
'WF': 'Wallis And Futuna',
|
|
||||||
'EH': 'Western Sahara',
|
|
||||||
'YE': 'Yemen',
|
|
||||||
'ZM': 'Zambia',
|
|
||||||
'ZW': 'Zimbabwe'
|
|
||||||
};
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
Table,
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
columns: [
|
|
||||||
{ key: "location", label: "Location" },
|
|
||||||
{ key: "name", label: "Name" },
|
|
||||||
{ key: "map", label: "Map" },
|
|
||||||
{ key: "players", label: "Players" },
|
|
||||||
],
|
|
||||||
tableData: [],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
getData() {
|
|
||||||
axios
|
|
||||||
.get(`https://backend.beammp.com/servers-info`)
|
|
||||||
.then(res => {
|
|
||||||
var servers = []
|
|
||||||
var data = res.data;
|
|
||||||
var i = 0
|
|
||||||
|
|
||||||
var serversArray = new Array();
|
|
||||||
// Parse the data to a nice looking Array
|
|
||||||
for (var i = 0; i < data.length; i++) {
|
|
||||||
var v = data[i]
|
|
||||||
v.strippedName = this.stripCustomFormatting(v.sname);
|
|
||||||
serversArray.push(v);
|
|
||||||
}
|
|
||||||
// Sort the servers to display official servers first
|
|
||||||
serversArray.sort(function(a, b) {
|
|
||||||
if (a.official && b.official) return a.strippedName.localeCompare(b.strippedName)
|
|
||||||
else if (a.official) return -1;
|
|
||||||
else if (b.official) return 1;
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
serversArray.forEach(s => {
|
|
||||||
servers.push({id: i, cc: s.location, location: this.getCountryName(s.location), name: this.formatServerName(s.sname), map: this.smoothMapName(s.map), players: `${s.players}/${s.maxplayers}`, raw: s, style: this.getRowStyle(s)})
|
|
||||||
i++
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
this.tableData = servers//res.data
|
|
||||||
})
|
|
||||||
},
|
|
||||||
getRowStyle(server) {
|
|
||||||
const s = false ? 'rgba(255, 215, 0, 0.35)!important' : server.featured ? 'rgba(0, 128, 0, 0.25)!important' : server.official ? 'rgba(255, 106, 0, 0.25)!important' : server.partner ? 'rgba(0, 123, 195, 0.3)!important' : 'rgba(0, 0, 0, 0)!important';
|
|
||||||
return `background: ${s}`
|
|
||||||
},
|
|
||||||
stripCustomFormatting(name) {
|
|
||||||
var serverStyleArray = [
|
|
||||||
"^0",
|
|
||||||
"^1",
|
|
||||||
"^2",
|
|
||||||
"^3",
|
|
||||||
"^4",
|
|
||||||
"^5",
|
|
||||||
"^6",
|
|
||||||
"^7",
|
|
||||||
"^8",
|
|
||||||
"^9",
|
|
||||||
"^a",
|
|
||||||
"^b",
|
|
||||||
"^c",
|
|
||||||
"^d",
|
|
||||||
"^e",
|
|
||||||
"^f",
|
|
||||||
"^l",
|
|
||||||
"^m",
|
|
||||||
"^n",
|
|
||||||
"^o",
|
|
||||||
"^r",
|
|
||||||
"^p"
|
|
||||||
];
|
|
||||||
|
|
||||||
for (var i = 0; i < serverStyleArray.length; i++){
|
|
||||||
while (name.includes(serverStyleArray[i])){
|
|
||||||
name = name.replace(serverStyleArray[i], "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return name;
|
|
||||||
},
|
|
||||||
getCountryName(countryCode) {
|
|
||||||
if (isoCountries.hasOwnProperty(countryCode)) {
|
|
||||||
return isoCountries[countryCode];
|
|
||||||
} else {
|
|
||||||
return countryCode;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
toTitleCase(str) {
|
|
||||||
return str.replace(/\w\S*/g, function (txt) {
|
|
||||||
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
smoothMapName(map) {
|
|
||||||
if (map != "Any Map") {
|
|
||||||
map = map.replace("/info.json", "")
|
|
||||||
map = map.split('/').pop().replace(/\s*/g, '')
|
|
||||||
map = map.replace(/_/g, " ")
|
|
||||||
map = map.replace(/-/g, " ")
|
|
||||||
map = this.toTitleCase(map)
|
|
||||||
}
|
|
||||||
return map
|
|
||||||
},
|
|
||||||
applyCode(string, codes) {
|
|
||||||
var cssText = ''
|
|
||||||
string = string.replace(/\x00*/g, '');
|
|
||||||
for (var i = 0, len = codes.length; i < len; i++) {
|
|
||||||
cssText += styleMap[codes[i]] + ';';
|
|
||||||
}
|
|
||||||
return {s: string, f:cssText};
|
|
||||||
},
|
|
||||||
formatServerName(string) {
|
|
||||||
var codes = string.match(/\^.{1}/g) || [],
|
|
||||||
indexes = [],
|
|
||||||
apply = [],
|
|
||||||
tmpStr,
|
|
||||||
name = [],
|
|
||||||
i,
|
|
||||||
len;
|
|
||||||
for (i = 0, len = codes.length; i < len; i++) {
|
|
||||||
indexes.push(string.indexOf(codes[i]));
|
|
||||||
string = string.replace(codes[i], '\x00\x00');
|
|
||||||
}
|
|
||||||
if (indexes[0] !== 0) {
|
|
||||||
name.push(this.applyCode(string.substring(0, indexes[0]), []))
|
|
||||||
}
|
|
||||||
for (i = 0; i < len; i++) {
|
|
||||||
var indexDelta = indexes[i + 1] - indexes[i];
|
|
||||||
if (indexDelta === 2) {
|
|
||||||
while (indexDelta === 2) {
|
|
||||||
apply.push(codes[i]);
|
|
||||||
i++;
|
|
||||||
indexDelta = indexes[i + 1] - indexes[i];
|
|
||||||
}
|
|
||||||
apply.push(codes[i]);
|
|
||||||
} else {
|
|
||||||
apply.push(codes[i]);
|
|
||||||
}
|
|
||||||
if (apply.lastIndexOf('^r') > -1) {
|
|
||||||
apply = apply.slice(apply.lastIndexOf('^r') + 1);
|
|
||||||
}
|
|
||||||
tmpStr = string.substring(indexes[i], indexes[i + 1]);
|
|
||||||
name.push(this.applyCode(tmpStr, apply))
|
|
||||||
}
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
},
|
|
||||||
beforeMount() {
|
|
||||||
this.getData()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="container mx-auto mt-8">
|
|
||||||
<h1 class="text-4xl font-bold mb-4">Project Metrics</h1>
|
|
||||||
|
|
||||||
<!-- Timeseries Graph -->
|
|
||||||
<div v-if="dataLoaded">
|
|
||||||
<Plotly :data="graphData" :layout="graphLayout" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Metrics -->
|
|
||||||
<div class="mt-8">
|
|
||||||
<p><strong>Concurrent Players (All Time High):</strong> {{ allTimeHigh }}</p>
|
|
||||||
<p><strong>Players Online Now:</strong> {{ playersOnlineNow }}</p>
|
|
||||||
<p><strong>Servers Online Now:</strong> {{ serversOnlineNow }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import Plotly from '@/components/stats/Plotly.vue'
|
|
||||||
import axios from 'axios'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
Plotly,
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
// Sample Data
|
|
||||||
graphData: [
|
|
||||||
{
|
|
||||||
x: ['2023-01-01', '2023-01-02', '2023-01-03'],
|
|
||||||
y: [10, 15, 8],
|
|
||||||
type: 'scatter',
|
|
||||||
mode: 'lines+markers',
|
|
||||||
name: 'Concurrent Players',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
graphLayout: {
|
|
||||||
title: 'Concurrent Players Over Time',
|
|
||||||
xaxis: {
|
|
||||||
autorange: true,
|
|
||||||
range: ['2015-02-17', '2017-02-16'],
|
|
||||||
rangeselector: {buttons: [
|
|
||||||
{
|
|
||||||
count: 24,
|
|
||||||
label: '24h',
|
|
||||||
step: 'hour',
|
|
||||||
stepmode: 'backward'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
count: 7,
|
|
||||||
label: '7d',
|
|
||||||
step: 'day',
|
|
||||||
stepmode: 'backward'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
count: 1,
|
|
||||||
label: '1m',
|
|
||||||
step: 'month',
|
|
||||||
stepmode: 'backward'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
count: 6,
|
|
||||||
label: '6m',
|
|
||||||
step: 'month',
|
|
||||||
stepmode: 'backward'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
count: 12,
|
|
||||||
label: '1y',
|
|
||||||
step: 'month',
|
|
||||||
stepmode: 'backward'
|
|
||||||
},
|
|
||||||
{step: 'all'}
|
|
||||||
]},
|
|
||||||
rangeslider: {range: ['2015-02-17', '2017-02-16']},
|
|
||||||
type: 'date'
|
|
||||||
},
|
|
||||||
yaxis: {
|
|
||||||
title: 'Concurrent Players',
|
|
||||||
autorange: true,
|
|
||||||
range: [86.8700008333, 138.870004167],
|
|
||||||
type: 'linear'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
allTimeHigh: 20,
|
|
||||||
playersOnlineNow: 12,
|
|
||||||
serversOnlineNow: 3,
|
|
||||||
dataLoaded: false,
|
|
||||||
timePeriod: 'today'
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
setTimeRange(range) {
|
|
||||||
// Update graph data based on the selected time range
|
|
||||||
// You need to implement the logic to fetch data from your backend here
|
|
||||||
console.log(`Updating graph for ${range}`);
|
|
||||||
},
|
|
||||||
getData() {
|
|
||||||
axios
|
|
||||||
.get(`https://backend.beammp.com/stats-info?period=${this.timePeriod}`)
|
|
||||||
.then(res => {
|
|
||||||
const data = [];
|
|
||||||
var Labels = [];
|
|
||||||
var Players = [];
|
|
||||||
if (res.data.v2history) {
|
|
||||||
res.data.v2history.forEach(function(item, index) {
|
|
||||||
Labels.push(item.datetime)
|
|
||||||
Players.push(item.players)
|
|
||||||
})
|
|
||||||
this.graphData[0].x = Labels
|
|
||||||
this.graphData[0].y = Players
|
|
||||||
this.allTimeHigh = res.data.maxp
|
|
||||||
console.log('Data Downloaded & Sorted')
|
|
||||||
this.dataLoaded = true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
beforeMount() {
|
|
||||||
this.getData();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
/* Add Tailwind CSS styles as needed */
|
|
||||||
</style>
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
<template>
|
|
||||||
<h1>BeamMP Terms & Conditions</h1>
|
|
||||||
</template>
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
/** @type {import('tailwindcss').Config} */
|
|
||||||
export default {
|
|
||||||
content: [
|
|
||||||
"./index.html",
|
|
||||||
"./src/**/*.{vue,js,ts,jsx,tsx}",
|
|
||||||
],
|
|
||||||
theme: {
|
|
||||||
extend: {},
|
|
||||||
},
|
|
||||||
plugins: [],
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -0,0 +1,735 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en" style="overflow-x: hidden;">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/ico+xml" href="/favicon.ico" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>BeamMP Website</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.js"></script>
|
||||||
|
<noscript>
|
||||||
|
<style>
|
||||||
|
/* NoScript Styling */
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||||
|
line-height: 1.6;
|
||||||
|
color: #333;
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning-banner {
|
||||||
|
background: linear-gradient(135deg, #f36d24 0%, #e85d1f 100%);
|
||||||
|
color: white;
|
||||||
|
padding: 1rem;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 600;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning-banner strong {
|
||||||
|
display: block;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
background: white;
|
||||||
|
border-bottom: 1px solid #e5e5e5;
|
||||||
|
padding: 1rem 0;
|
||||||
|
position: sticky;
|
||||||
|
top: 94px;
|
||||||
|
z-index: 999;
|
||||||
|
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-content {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 1rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
height: 64px;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav a {
|
||||||
|
color: #333;
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border-radius: 6px;
|
||||||
|
transition: background 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav a:hover {
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero {
|
||||||
|
background: linear-gradient(to bottom, rgba(0,0,0,0.5), rgba(0,0,0,0.7)), url('/landing-1.jpg') center/cover;
|
||||||
|
color: white;
|
||||||
|
padding: 5rem 1rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-content {
|
||||||
|
max-width: 900px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero h1 {
|
||||||
|
font-size: 3rem;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero p {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
color: #e5e5e5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cta-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 1rem 2rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
transition: transform 0.2s, box-shadow 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:hover {
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background: linear-gradient(135deg, #f36d24 0%, #dc2626 100%);
|
||||||
|
color: white;
|
||||||
|
box-shadow: 0 4px 12px rgba(243, 109, 36, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary {
|
||||||
|
background: rgba(255,255,255,0.1);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border: 1px solid rgba(255,255,255,0.3);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||||
|
gap: 2rem;
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-value {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #f36d24;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-label {
|
||||||
|
color: #e5e5e5;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 4rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section {
|
||||||
|
padding: 4rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-alt {
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-2 {
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-4 {
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background: white;
|
||||||
|
border: 1px solid #e5e5e5;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 2rem;
|
||||||
|
transition: border-color 0.2s, box-shadow 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card:hover {
|
||||||
|
border-color: #4470b6;
|
||||||
|
box-shadow: 0 8px 24px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-icon {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
color: #f36d24;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-text {
|
||||||
|
color: #666;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
background: white;
|
||||||
|
border-top: 1px solid #e5e5e5;
|
||||||
|
padding: 2rem 1rem;
|
||||||
|
margin-top: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-content {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.social-links {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.social-links a {
|
||||||
|
color: #666;
|
||||||
|
transition: color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.social-links a:hover {
|
||||||
|
color: #4470b6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-info {
|
||||||
|
text-align: right;
|
||||||
|
color: #666;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-links {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.75rem;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-links a {
|
||||||
|
color: #666;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-links a:hover {
|
||||||
|
color: #4470b6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.two-column {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 3rem;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-list {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-list li {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-bullet {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: rgba(68, 112, 182, 0.2);
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-top: 4px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-bullet::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #4470b6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-title {
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.screenshot {
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 12px;
|
||||||
|
border: 1px solid #e5e5e5;
|
||||||
|
box-shadow: 0 8px 24px rgba(0,0,0,0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cta-section {
|
||||||
|
background: linear-gradient(135deg, #f36d24 0%, #e85d1f 100%);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 3rem;
|
||||||
|
text-align: center;
|
||||||
|
color: white;
|
||||||
|
margin: 3rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cta-section h3 {
|
||||||
|
font-size: 2rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cta-section p {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
opacity: 0.95;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-white {
|
||||||
|
background: white;
|
||||||
|
color: #f36d24;
|
||||||
|
padding: 1rem 2rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 600;
|
||||||
|
display: inline-block;
|
||||||
|
transition: background 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-white:hover {
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.hero h1 {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero p {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.two-column {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-content {
|
||||||
|
flex-direction: column;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-info {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav {
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-content {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-content {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
min-width: 155px;
|
||||||
|
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
|
||||||
|
text-align: left;
|
||||||
|
color: black;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown:hover .dropdown-content {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-content > div {
|
||||||
|
color: black;
|
||||||
|
border-bottom: solid 1px #e5e5e5;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<!-- Warning Banner -->
|
||||||
|
<div class="warning-banner">
|
||||||
|
<strong>⚠️ JavaScript is Disabled</strong>
|
||||||
|
<span>This is a limited version of the BeamMP website. Please enable JavaScript for the full interactive experience.</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Header -->
|
||||||
|
<header class="header">
|
||||||
|
<div class="header-content">
|
||||||
|
<img src="/src/assets/BeamMP_blk.png" alt="BeamMP Logo" class="logo">
|
||||||
|
<nav class="nav">
|
||||||
|
<a href="https://forum.beammp.com">Forum</a>
|
||||||
|
<a href="https://docs.beammp.com">Docs</a>
|
||||||
|
<a href="https://github.com/BeamMP/BeamMP">GitHub</a>
|
||||||
|
<a href="https://www.patreon.com/BeamMP">Patreon</a>
|
||||||
|
<a href="https://discord.gg/beammp">Discord</a>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- Hero Section -->
|
||||||
|
<section class="hero">
|
||||||
|
<div class="hero-content">
|
||||||
|
<h1>Multiplayer Mod for <em>BeamNG.drive</em></h1>
|
||||||
|
<p>Drive together in the ultimate soft-body physics sandbox</p>
|
||||||
|
|
||||||
|
<div class="cta-buttons">
|
||||||
|
<a href="/installer/BeamMP_Installer.zip" class="btn btn-primary" download>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-download-icon lucide-download"><path d="M12 15V3"/><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="m7 10 5 5 5-5"/></svg> Download Now
|
||||||
|
</a>
|
||||||
|
<a href="https://forum.beammp.com/c/server-list/13" class="btn btn-secondary">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-server-icon lucide-server"><rect width="20" height="8" x="2" y="2" rx="2" ry="2"/><rect width="20" height="8" x="2" y="14" rx="2" ry="2"/><line x1="6" x2="6.01" y1="6" y2="6"/><line x1="6" x2="6.01" y1="18" y2="18"/></svg> Browse Servers
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stats">
|
||||||
|
<div class="stat">
|
||||||
|
<div class="stat-value">2,000+</div>
|
||||||
|
<div class="stat-label">Active Players</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat">
|
||||||
|
<div class="stat-value">500+</div>
|
||||||
|
<div class="stat-label">Public Servers</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat">
|
||||||
|
<div class="stat-value">2M+</div>
|
||||||
|
<div class="stat-label">All Servers</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Features Section -->
|
||||||
|
<section class="section section-alt">
|
||||||
|
<div class="container">
|
||||||
|
<h2 class="section-title">Why Choose BeamMP?</h2>
|
||||||
|
<div class="grid grid-4">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-server-icon lucide-server"><rect width="20" height="8" x="2" y="2" rx="2" ry="2"/><rect width="20" height="8" x="2" y="14" rx="2" ry="2"/><line x1="6" x2="6.01" y1="6" y2="6"/><line x1="6" x2="6.01" y1="18" y2="18"/></svg></div>
|
||||||
|
<h3 class="card-title">Stable Servers</h3>
|
||||||
|
<p class="card-text">Rock-solid server performance with minimal lag and maximum uptime for the best multiplayer experience.</p>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-package-icon lucide-package"><path d="M11 21.73a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73z"/><path d="M12 22V12"/><polyline points="3.29 7 12 12 20.71 7"/><path d="m7.5 4.27 9 5.15"/></svg></div>
|
||||||
|
<h3 class="card-title">BeamNG.drive Required</h3>
|
||||||
|
<p class="card-text">Built specifically for BeamNG.drive, leveraging its incredible soft-body physics engine.</p>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-zap-icon lucide-zap"><path d="M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z"/></svg></div>
|
||||||
|
<h3 class="card-title">Standalone Client</h3>
|
||||||
|
<p class="card-text">Easy-to-use launcher that manages everything for you - just install and play.</p>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-globe-icon lucide-globe"><circle cx="12" cy="12" r="10"/><path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"/><path d="M2 12h20"/></svg></div>
|
||||||
|
<h3 class="card-title">Real-time Sync</h3>
|
||||||
|
<p class="card-text">Advanced synchronization technology ensures smooth gameplay with players around the world.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Communities Section -->
|
||||||
|
<section class="section">
|
||||||
|
<div class="container">
|
||||||
|
<h2 class="section-title">Join Vibrant Communities</h2>
|
||||||
|
<p style="text-align: center; color: #666; margin-bottom: 3rem; font-size: 1.1rem;">
|
||||||
|
From casual cruising to competitive racing, find your perfect server
|
||||||
|
</p>
|
||||||
|
<div class="grid grid-4">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-rocket-icon lucide-rocket"><path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z"/><path d="m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z"/><path d="M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0"/><path d="M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5"/></svg></div>
|
||||||
|
<h3 class="card-title">Racing</h3>
|
||||||
|
<p class="card-text">Compete in high-speed races with custom tracks and competitive leaderboards.</p>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-gamepad2-icon lucide-gamepad-2"><line x1="6" x2="10" y1="11" y2="11"/><line x1="8" x2="8" y1="9" y2="13"/><line x1="15" x2="15.01" y1="12" y2="12"/><line x1="18" x2="18.01" y1="10" y2="10"/><path d="M17.32 5H6.68a4 4 0 0 0-3.978 3.59c-.006.052-.01.101-.017.152C2.604 9.416 2 14.456 2 16a3 3 0 0 0 3 3c1 0 1.5-.5 2-1l1.414-1.414A2 2 0 0 1 9.828 16h4.344a2 2 0 0 1 1.414.586L17 18c.5.5 1 1 2 1a3 3 0 0 0 3-3c0-1.545-.604-6.584-.685-7.258-.007-.05-.011-.1-.017-.151A4 4 0 0 0 17.32 5z"/></svg></div>
|
||||||
|
<h3 class="card-title">Roleplay</h3>
|
||||||
|
<p class="card-text">Immerse yourself in realistic roleplay scenarios with dedicated communities.</p>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-shield-icon lucide-shield"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"/></svg></div>
|
||||||
|
<h3 class="card-title">Crash & Derby</h3>
|
||||||
|
<p class="card-text">Destruction enthusiasts unite! Experience epic demolition derby events.</p>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-globe-icon lucide-globe"><circle cx="12" cy="12" r="10"/><path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"/><path d="M2 12h20"/></svg></div>
|
||||||
|
<h3 class="card-title">Freeroam</h3>
|
||||||
|
<p class="card-text">Explore vast maps with friends in relaxed freeroam servers.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Server Browser Section -->
|
||||||
|
<section class="section section-alt">
|
||||||
|
<div class="container">
|
||||||
|
<div class="two-column">
|
||||||
|
<div>
|
||||||
|
<h2 style="font-size: 2.5rem; font-weight: bold; margin-bottom: 1.5rem;">Find Your Perfect Server</h2>
|
||||||
|
<p style="font-size: 1.1rem; color: #666; margin-bottom: 2rem;">
|
||||||
|
Browse hundreds of unique servers with different game modes, maps, and communities. There's something for everyone!
|
||||||
|
</p>
|
||||||
|
<ul class="feature-list">
|
||||||
|
<li>
|
||||||
|
<div class="feature-bullet"></div>
|
||||||
|
<div>
|
||||||
|
<div class="feature-title">Custom Game Modes</div>
|
||||||
|
<div class="card-text">From racing leagues to roleplay servers</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<div class="feature-bullet"></div>
|
||||||
|
<div>
|
||||||
|
<div class="feature-title">Active Moderation</div>
|
||||||
|
<div class="card-text">Safe and welcoming communities</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<div class="feature-bullet"></div>
|
||||||
|
<div>
|
||||||
|
<div class="feature-title">Global Network</div>
|
||||||
|
<div class="card-text">Servers worldwide for the best connection</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<a href="https://forum.beammp.com/c/server-list/13" class="btn btn-primary">
|
||||||
|
Browse All Servers →
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<img src="/beammpservers.png" alt="BeamMP Server Browser" class="screenshot">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Developer Section -->
|
||||||
|
<section class="section">
|
||||||
|
<div class="container">
|
||||||
|
<h2 class="section-title">Built for Developers</h2>
|
||||||
|
<p style="text-align: center; color: #666; margin-bottom: 3rem; font-size: 1.1rem;">
|
||||||
|
Powerful tools and extensive documentation to create your own server experiences
|
||||||
|
</p>
|
||||||
|
<div class="grid grid-4">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-code-icon lucide-code"><path d="m16 18 6-6-6-6"/><path d="m8 6-6 6 6 6"/></svg></div>
|
||||||
|
<h3 class="card-title">Lua Scripting</h3>
|
||||||
|
<p class="card-text">Create custom game modes and server-side mods with our powerful Lua API.</p>
|
||||||
|
<a href="https://docs.beammp.com/scripting/mod-reference/" style="color: #4470b6; font-weight: 600; text-decoration: none;">Learn More →</a>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-book-open-icon lucide-book-open"><path d="M12 7v14"/><path d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"/></svg></div>
|
||||||
|
<h3 class="card-title">Comprehensive Docs</h3>
|
||||||
|
<p class="card-text">Detailed guides and API references to help you get started quickly.</p>
|
||||||
|
<a href="https://docs.beammp.com" style="color: #4470b6; font-weight: 600; text-decoration: none;">Learn More →</a>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-wrench-icon lucide-wrench"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.106-3.105c.32-.322.863-.22.983.218a6 6 0 0 1-8.259 7.057l-7.91 7.91a1 1 0 0 1-2.999-3l7.91-7.91a6 6 0 0 1 7.057-8.259c.438.12.54.662.219.984z"/></svg></div>
|
||||||
|
<h3 class="card-title">Open Source</h3>
|
||||||
|
<p class="card-text">Contribute to the project or learn from our codebase on GitHub.</p>
|
||||||
|
<a href="https://github.com/BeamMP" style="color: #4470b6; font-weight: 600; text-decoration: none;">Learn More →</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Hosting CTA -->
|
||||||
|
<div class="cta-section">
|
||||||
|
<h3>Ready to Host Your Own Server?</h3>
|
||||||
|
<p>Check out our trusted hosting partners or download the server software to host it yourself.</p>
|
||||||
|
<div class="flex flex-col sm:flex-row gap-4 justify-center pt-4">
|
||||||
|
<a
|
||||||
|
href="https://github.com/BeamMP/BeamMP-Server/releases/latest/download/BeamMP-Server.exe"
|
||||||
|
class="btn-white flex items-center justify-center gap-3 bg-neutral-800 hover:bg-neutral-700 dark:bg-neutral-700 dark:hover:bg-neutral-600 text-white border border-neutral-600 dark:border-neutral-600 px-6 py-3 rounded-lg font-semibold transition-all"
|
||||||
|
>
|
||||||
|
Windows
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-download-icon lucide-download"><path d="M12 15V3"/><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="m7 10 5 5 5-5"/></svg>
|
||||||
|
</a>
|
||||||
|
<span
|
||||||
|
class="dropdown btn-white flex items-center justify-center gap-3 bg-neutral-800 hover:bg-neutral-700 dark:bg-neutral-700 dark:hover:bg-neutral-600 text-white border border-neutral-600 dark:border-neutral-600 px-6 py-3 rounded-lg font-semibold transition-all"
|
||||||
|
>
|
||||||
|
Linux Builds
|
||||||
|
<div class="dropdown-content">
|
||||||
|
<div>
|
||||||
|
<span style="color: #666; font-size: 0.85rem; padding: 0.5rem 1rem; display: block; font-weight: 600;">x86_64</span>
|
||||||
|
</div>
|
||||||
|
<div style="padding: 0.5rem 1rem;">
|
||||||
|
<a href="https://github.com/BeamMP/BeamMP-Server/releases/latest/download/BeamMP-Server.debian.11.x86_64" style="color: #333; text-decoration: none; display: flex; align-items: center; gap: 0.5rem; padding: 0.25rem 0;">
|
||||||
|
Debian 11
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-download-icon lucide-download"><path d="M12 15V3"/><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="m7 10 5 5 5-5"/></svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div style="padding: 0.5rem 1rem;">
|
||||||
|
<a href="https://github.com/BeamMP/BeamMP-Server/releases/latest/download/BeamMP-Server.debian.12.x86_64" style="color: #333; text-decoration: none; display: flex; align-items: center; gap: 0.5rem; padding: 0.25rem 0;">
|
||||||
|
Debian 12
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-download-icon lucide-download"><path d="M12 15V3"/><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="m7 10 5 5 5-5"/></svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div style="padding: 0.5rem 1rem;">
|
||||||
|
<a href="https://github.com/BeamMP/BeamMP-Server/releases/latest/download/BeamMP-Server.ubuntu.22.04.x86_64" style="color: #333; text-decoration: none; display: flex; align-items: center; gap: 0.5rem; padding: 0.25rem 0;">
|
||||||
|
Ubuntu 22.04
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-download-icon lucide-download"><path d="M12 15V3"/><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="m7 10 5 5 5-5"/></svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div style="padding: 0.5rem 1rem;">
|
||||||
|
<a href="https://github.com/BeamMP/BeamMP-Server/releases/latest/download/BeamMP-Server.ubuntu.24.04.x86_64" style="color: #333; text-decoration: none; display: flex; align-items: center; gap: 0.5rem; padding: 0.25rem 0;">
|
||||||
|
Ubuntu 24.04
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-download-icon lucide-download"><path d="M12 15V3"/><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="m7 10 5 5 5-5"/></svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span style="color: #666; font-size: 0.85rem; padding: 0.5rem 1rem; display: block; font-weight: 600;">arm64</span>
|
||||||
|
</div>
|
||||||
|
<div style="padding: 0.5rem 1rem;">
|
||||||
|
<a href="https://github.com/BeamMP/BeamMP-Server/releases/latest/download/BeamMP-Server.debian.11.arm64" style="color: #333; text-decoration: none; display: flex; align-items: center; gap: 0.5rem; padding: 0.25rem 0;">
|
||||||
|
Debian 11
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-download-icon lucide-download"><path d="M12 15V3"/><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="m7 10 5 5 5-5"/></svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div style="padding: 0.5rem 1rem;">
|
||||||
|
<a href="https://github.com/BeamMP/BeamMP-Server/releases/latest/download/BeamMP-Server.debian.12.arm64" style="color: #333; text-decoration: none; display: flex; align-items: center; gap: 0.5rem; padding: 0.25rem 0;">
|
||||||
|
Debian 12
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-download-icon lucide-download"><path d="M12 15V3"/><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="m7 10 5 5 5-5"/></svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div style="padding: 0.5rem 1rem;">
|
||||||
|
<a href="https://github.com/BeamMP/BeamMP-Server/releases/latest/download/BeamMP-Server.ubuntu.22.04.arm64" style="color: #333; text-decoration: none; display: flex; align-items: center; gap: 0.5rem; padding: 0.25rem 0;">
|
||||||
|
Ubuntu 22.04
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-download-icon lucide-download"><path d="M12 15V3"/><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="m7 10 5 5 5-5"/></svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div style="padding: 0.5rem 1rem;">
|
||||||
|
<a href="https://github.com/BeamMP/BeamMP-Server/releases/latest/download/BeamMP-Server.ubuntu.24.04.arm64" style="color: #333; text-decoration: none; display: flex; align-items: center; gap: 0.5rem; padding: 0.25rem 0;">
|
||||||
|
Ubuntu 24.04
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-download-icon lucide-download"><path d="M12 15V3"/><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="m7 10 5 5 5-5"/></svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<footer class="footer">
|
||||||
|
<div class="footer-content">
|
||||||
|
<div class="social-links">
|
||||||
|
<a href="https://github.com/BeamMP" aria-label="GitHub">
|
||||||
|
<svg width="20" height="20" fill="currentColor" viewBox="0 0 24 24"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
|
||||||
|
</a>
|
||||||
|
<a href="https://discord.gg/beammp" aria-label="Discord">
|
||||||
|
<svg width="20" height="20" fill="currentColor" viewBox="0 0 24 24"><path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028 14.09 14.09 0 0 0 1.226-1.994.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z"/></svg>
|
||||||
|
</a>
|
||||||
|
<a href="https://www.youtube.com/@beammpofficial" aria-label="YouTube">
|
||||||
|
<svg width="20" height="20" fill="currentColor" viewBox="0 0 24 24"><path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"/></svg>
|
||||||
|
</a>
|
||||||
|
<a href="https://x.com/beammpofficial" aria-label="X (Twitter)">
|
||||||
|
<svg width="20" height="20" fill="currentColor" viewBox="0 0 24 24"><path d="M18.901 1.153h3.68l-8.04 9.19L24 22.846h-7.406l-5.8-7.584-6.638 7.584H.474l8.6-9.83L0 1.154h7.594l5.243 6.932ZM17.61 20.644h2.039L6.486 3.24H4.298Z"/></svg>
|
||||||
|
</a>
|
||||||
|
<a href="https://www.reddit.com/r/BeamMP" aria-label="Reddit">
|
||||||
|
<svg width="20" height="20" fill="currentColor" viewBox="0 0 24 24"><path d="M12 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0zm5.01 4.744c.688 0 1.25.561 1.25 1.249a1.25 1.25 0 0 1-2.498.056l-2.597-.547-.8 3.747c1.824.07 3.48.632 4.674 1.488.308-.309.73-.491 1.207-.491.968 0 1.754.786 1.754 1.754 0 .716-.435 1.333-1.01 1.614a3.111 3.111 0 0 1 .042.52c0 2.694-3.13 4.87-7.004 4.87-3.874 0-7.004-2.176-7.004-4.87 0-.183.015-.366.043-.534A1.748 1.748 0 0 1 4.028 12c0-.968.786-1.754 1.754-1.754.463 0 .898.196 1.207.49 1.207-.883 2.878-1.43 4.744-1.487l.885-4.182a.342.342 0 0 1 .14-.197.35.35 0 0 1 .238-.042l2.906.617a1.214 1.214 0 0 1 1.108-.701zM9.25 12C8.561 12 8 12.562 8 13.25c0 .687.561 1.248 1.25 1.248.687 0 1.248-.561 1.248-1.249 0-.688-.561-1.249-1.249-1.249zm5.5 0c-.687 0-1.248.561-1.248 1.25 0 .687.561 1.248 1.249 1.248.688 0 1.249-.561 1.249-1.249 0-.687-.562-1.249-1.25-1.249zm-5.466 3.99a.327.327 0 0 0-.231.094.33.33 0 0 0 0 .463c.842.842 2.484.913 2.961.913.477 0 2.105-.056 2.961-.913a.361.361 0 0 0 .029-.463.33.33 0 0 0-.464 0c-.547.533-1.684.73-2.512.73-.828 0-1.979-.196-2.512-.73a.326.326 0 0 0-.232-.095z"/></svg>
|
||||||
|
</a>
|
||||||
|
<span style="color: #ccc;">|</span>
|
||||||
|
<a href="https://www.patreon.com/BeamMP" style="color: #f96854; font-size: 0.85rem; text-decoration: underline;">
|
||||||
|
Support on Patreon
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer-info">
|
||||||
|
<p>© 2019 - 2025 | BeamMP Mod Team All Rights Reserved</p>
|
||||||
|
<div class="footer-links">
|
||||||
|
<a href="https://forum.beammp.com/topic/95/privacy-policy-v1-0">Privacy Policy</a>
|
||||||
|
<span>·</span>
|
||||||
|
<a href="https://forum.beammp.com/topic/94/terms-of-use-v1-0">Terms of Use</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</noscript>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
/*
|
|
||||||
* oooooooooo. ooo ooooo ooooooooo.
|
|
||||||
* `888' `Y8b `88. .888' `888 `Y88.
|
|
||||||
* 888 888 .ooooo. .oooo. ooo. .oo. .oo. 888b d'888 888 .d88'
|
|
||||||
* 888oooo888' d88' `88b `P )88b `888P"Y88bP"Y88b 8 Y88. .P 888 888ooo88P'
|
|
||||||
* 888 `88b 888ooo888 .oP"888 888 888 888 8 `888' 888 888
|
|
||||||
* 888 .88P 888 .o d8( 888 888 888 888 8 Y 888 888
|
|
||||||
* o888bood8P' `Y8bod8P' `Y888""8o o888o o888o o888o o8o o888o o888o
|
|
||||||
* ========================================================================
|
|
||||||
* Copyright (c) 2019-2023 BeamMP Ltd. All rights reserved.
|
|
||||||
*/
|
|
||||||
|
|
||||||
require('dotenv').config()
|
|
||||||
const pkg = require('./package.json')
|
|
||||||
const chalk = require('chalk');
|
|
||||||
const cluster = require('cluster');
|
|
||||||
|
|
||||||
const error = chalk.bold.keyword('red');
|
|
||||||
const warn = chalk.keyword('orange');
|
|
||||||
const good = chalk.keyword('lime');
|
|
||||||
|
|
||||||
process.on('warning', (warning) => {
|
|
||||||
console.log(warning.stack);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (cluster.isMaster) {
|
|
||||||
|
|
||||||
const env = process.env.NODE_ENV || 'development'
|
|
||||||
|
|
||||||
console.log('oooooooooo. ooo ooooo ooooooooo. ')
|
|
||||||
console.log('`888\' `Y8b `88. .888\' `888 `Y88. ')
|
|
||||||
console.log(' 888 888 .ooooo. .oooo. ooo. .oo. .oo. 888b d\'888 888 .d88\' ')
|
|
||||||
console.log(' 888oooo888\' d88\' `88b `P )88b `888P"Y88bP"Y88b 8 Y88. .P 888 888ooo88P\' ')
|
|
||||||
console.log(' 888 `88b 888ooo888 .oP"888 888 888 888 8 `888\' 888 888 ')
|
|
||||||
console.log(' 888 .88P 888 .o d8( 888 888 888 888 8 Y 888 888 ')
|
|
||||||
console.log('o888bood8P\' `Y8bod8P\' `Y888""8o o888o o888o o888o o8o o888o o888o ')
|
|
||||||
console.log('=================================================================================')
|
|
||||||
console.log('Website v' + pkg.version + ' Copyright (C) 2019-2024 BeamMP Ltd')
|
|
||||||
console.log('')
|
|
||||||
console.log('Running in: ' + env)
|
|
||||||
console.log('Server Time: ' + new Date())
|
|
||||||
|
|
||||||
function start() {
|
|
||||||
process.title = pkg.name + "@" + pkg.version;
|
|
||||||
|
|
||||||
|
|
||||||
if (cluster.isMaster) {
|
|
||||||
console.log(`Master PID: ${process.pid}`)
|
|
||||||
console.log(`Creating ${process.env.INSTANCES} Instances of the Website Backend`)
|
|
||||||
for (let i = 0; i < process.env.INSTANCES; i++) {
|
|
||||||
cluster.fork();
|
|
||||||
}
|
|
||||||
// set console's directory so we can see output from workers
|
|
||||||
console.dir(cluster.workers, { depth: 0 });
|
|
||||||
|
|
||||||
cluster.on('exit', (worker, code) => {
|
|
||||||
// Good exit code is 0 :))
|
|
||||||
// exitedAfterDisconnect ensures that it is not killed by master cluster or manually
|
|
||||||
// if we kill it via .kill or .disconnect it will be set to true
|
|
||||||
// \x1b[XXm represents a color, and [0m represent the end of this
|
|
||||||
//color in the console ( 0m sets it to white again )
|
|
||||||
if (code !== 0 && !worker.exitedAfterDisconnect) {
|
|
||||||
console.error(`\x1b[34mWorker ${worker.process.pid} crashed... Starting a new worker...\x1b[0m`);
|
|
||||||
const nw = cluster.fork();
|
|
||||||
console.error(`\x1b[32mWorker ${nw.process.pid} will replace him \x1b[0m`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
console.error('FATAL: This script can only be run as a master process.')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
start()
|
|
||||||
} else {
|
|
||||||
const ws = require('./src/webserver')
|
|
||||||
|
|
||||||
try {
|
|
||||||
ws.init(function (err) {
|
|
||||||
if (err) {
|
|
||||||
console.error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ws.listen(function () {
|
|
||||||
console.info('BeamMP Website Ready')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
} catch (e) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
|
||||||
|
server_name _;
|
||||||
|
server_tokens off;
|
||||||
|
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
# Baseline security hardening for a static SPA.
|
||||||
|
# NOTE: add_header directives are NOT inherited by child location blocks that
|
||||||
|
# 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'; style-src 'self' 'unsafe-inline'; font-src 'self' data:; connect-src 'self' https://backend.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
# Return 404 if a file is genuinely missing rather than silently serving index.html.
|
||||||
|
location ~* \.(js|css|woff2?|ttf|eot|svg|webp|avif|png|jpg|jpeg|gif|ico|map)$ {
|
||||||
|
try_files $uri =404;
|
||||||
|
expires 1y;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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;
|
||||||
|
}
|
||||||
@@ -1,29 +1,38 @@
|
|||||||
{
|
{
|
||||||
"name": "beammp-website",
|
"name": "beammp-website",
|
||||||
"version": "1.0.1",
|
"private": true,
|
||||||
"description": "BeamMP Website",
|
"version": "2.4.16",
|
||||||
"main": "index.js",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"docker-build": "docker build -t 192.168.100.6:5000/beammp/website:latest -t 192.168.100.6:5000/beammp/website:1.0.1 .",
|
"dev": "vite",
|
||||||
"docker-push": "docker push 192.168.100.6:5000/beammp/website:latest"
|
"build": "vite build",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"lint": "eslint . --fix",
|
||||||
|
"format": "prettier --write \"src/**/*.{js,vue,css,json}\""
|
||||||
},
|
},
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git+https://github.com/BeamMP/BeamMP-Website.git"
|
|
||||||
},
|
|
||||||
"author": "",
|
|
||||||
"license": "ISC",
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/BeamMP/BeamMP-Website/issues"
|
|
||||||
},
|
|
||||||
"homepage": "https://github.com/BeamMP/BeamMP-Website#readme",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"body-parser": "^1.19.0",
|
"@tailwindcss/vite": "^4.1.17",
|
||||||
"chalk": "^3.0.0",
|
"@vueuse/core": "^14.1.0",
|
||||||
"dotenv": "^16.0.3",
|
"class-variance-authority": "^0.7.1",
|
||||||
"ejs": "^3.0.1",
|
"clsx": "^2.1.1",
|
||||||
"express": "^4.17.1",
|
"lucide-vue-next": "^0.555.0",
|
||||||
"helmet": "^6.1.5",
|
"reka-ui": "^2.7.0",
|
||||||
"morgan": "^1.10.0"
|
"tailwind-merge": "^3.4.0",
|
||||||
|
"tailwindcss": "^4.1.17",
|
||||||
|
"tailwindcss-animate": "^1.0.7",
|
||||||
|
"vue": "^3.5.25",
|
||||||
|
"vue-i18n": "^11.2.2",
|
||||||
|
"vue-router": "^4.6.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-vue": "^6.0.2",
|
||||||
|
"@vue/eslint-config-prettier": "^10.2.0",
|
||||||
|
"eslint": "^9.39.1",
|
||||||
|
"eslint-config-prettier": "^10.1.8",
|
||||||
|
"eslint-plugin-prettier": "^5.5.4",
|
||||||
|
"eslint-plugin-vue": "^10.6.2",
|
||||||
|
"prettier": "^3.7.3",
|
||||||
|
"tw-animate-css": "^1.4.0",
|
||||||
|
"vite": "^7.2.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 5.1 MiB |
|
Before Width: | Height: | Size: 2.7 MiB After Width: | Height: | Size: 2.7 MiB |
|
Before Width: | Height: | Size: 1.9 MiB After Width: | Height: | Size: 1.9 MiB |
|
After Width: | Height: | Size: 108 KiB |
@@ -0,0 +1,86 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Horizon Hosting",
|
||||||
|
"website": "https://hrzn.link/beammp",
|
||||||
|
"from": "$3.34/mo",
|
||||||
|
"logo": "https://hrznhosting.com/assets/logo.svg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "RackGenius",
|
||||||
|
"website": "https://rackgeni.us/beammp-plans",
|
||||||
|
"from": "$0.5/mo",
|
||||||
|
"logo": "https://rackgenius.com/rackgenius-logo.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Connect Hosting",
|
||||||
|
"website": "https://connecthosting.net/beammp",
|
||||||
|
"from": "$1.49/mo",
|
||||||
|
"logo": "https://connecthosting.net/img/logo.webp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Assetto Hosting",
|
||||||
|
"website": "https://assettohosting.com/games/beamng",
|
||||||
|
"from": "$2.30/mo",
|
||||||
|
"logo": "https://assettohosting.com/_next/image?url=https%3A%2F%2Fstrapi.assettohosting.com%2Fuploads%2Flogo_2228c8bbfb.png&w=640&q=100"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Zap-Hosting",
|
||||||
|
"website": "https://zap-hosting.com/itsbeammp",
|
||||||
|
"from": "$6.46/mo",
|
||||||
|
"logo": "https://wormhole.ifyouwantmorepower.com/online/assets/beammp/zap-logo.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "HostHavoc",
|
||||||
|
"website": "https://hosthavoc.com",
|
||||||
|
"from": "$3.75/mo",
|
||||||
|
"logo": "https://hosthavoc.com/images/logo.svg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "PedalHost",
|
||||||
|
"website": "https://pedal.host",
|
||||||
|
"from": "$1.31/mo",
|
||||||
|
"logo": "https://pedal.host/pedalhost_horizontal_light.svg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Vyper Hosting",
|
||||||
|
"website": "https://vyperhosting.com/r/beammp",
|
||||||
|
"from": "$2.68/mo",
|
||||||
|
"logo": "https://vyperhosting.com/assets/logo.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "BisecHosting",
|
||||||
|
"website": "https://bisecthosting.com/beammp-server-hosting",
|
||||||
|
"from": "$5.99/mo",
|
||||||
|
"logo": "https://www.bisecthosting.com/_ipx/q_100&s_140x46/images/logo-dark-theme.svg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Four Season Hosting",
|
||||||
|
"website": "https://fourseasonshosting.com",
|
||||||
|
"from": "$3.00/mo",
|
||||||
|
"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",
|
||||||
|
"website": "https://winheberg.fr/offres/gaming/beammp",
|
||||||
|
"from": "$2.99/mo",
|
||||||
|
"logo": "https://winheberg.fr/img/logo-white.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Wabbanode",
|
||||||
|
"website": "https://wabbanode.com/partner/beammp",
|
||||||
|
"from": "$5.96/mo",
|
||||||
|
"logo": "https://wabbanode.com/_nuxt/wabba_logo.UPNIVeXa.webp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Iceline Hosting",
|
||||||
|
"website": "https://iceline-hosting.com/games/beammp",
|
||||||
|
"from": "$3.64/mo",
|
||||||
|
"logo": "https://iceline-hosting.com/_astro/logo.DTnWV5zL_157TDr.webp"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<script setup>
|
||||||
|
import DefaultLayout from './layouts/DefaultLayout.vue'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DefaultLayout />
|
||||||
|
</template>
|
||||||
|
After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 120 KiB |
|
Before Width: | Height: | Size: 207 B After Width: | Height: | Size: 207 B |
|
Before Width: | Height: | Size: 280 B After Width: | Height: | Size: 280 B |
|
Before Width: | Height: | Size: 122 B After Width: | Height: | Size: 122 B |
|
Before Width: | Height: | Size: 339 B After Width: | Height: | Size: 339 B |
|
Before Width: | Height: | Size: 362 B After Width: | Height: | Size: 362 B |
|
Before Width: | Height: | Size: 369 B After Width: | Height: | Size: 369 B |
|
Before Width: | Height: | Size: 217 B After Width: | Height: | Size: 217 B |
|
Before Width: | Height: | Size: 114 B After Width: | Height: | Size: 114 B |
|
Before Width: | Height: | Size: 235 B After Width: | Height: | Size: 235 B |
|
Before Width: | Height: | Size: 239 B After Width: | Height: | Size: 239 B |
|
Before Width: | Height: | Size: 162 B After Width: | Height: | Size: 162 B |
|
Before Width: | Height: | Size: 448 B After Width: | Height: | Size: 448 B |
|
Before Width: | Height: | Size: 104 B After Width: | Height: | Size: 104 B |
|
Before Width: | Height: | Size: 312 B After Width: | Height: | Size: 312 B |
|
Before Width: | Height: | Size: 166 B After Width: | Height: | Size: 166 B |
|
Before Width: | Height: | Size: 153 B After Width: | Height: | Size: 153 B |
|
Before Width: | Height: | Size: 167 B After Width: | Height: | Size: 167 B |
|
Before Width: | Height: | Size: 235 B After Width: | Height: | Size: 235 B |
|
Before Width: | Height: | Size: 179 B After Width: | Height: | Size: 179 B |
|
Before Width: | Height: | Size: 190 B After Width: | Height: | Size: 190 B |
|
Before Width: | Height: | Size: 114 B After Width: | Height: | Size: 114 B |
|
Before Width: | Height: | Size: 145 B After Width: | Height: | Size: 145 B |
|
Before Width: | Height: | Size: 117 B After Width: | Height: | Size: 117 B |
|
Before Width: | Height: | Size: 125 B After Width: | Height: | Size: 125 B |
|
Before Width: | Height: | Size: 383 B After Width: | Height: | Size: 383 B |
|
Before Width: | Height: | Size: 102 B After Width: | Height: | Size: 102 B |
|
Before Width: | Height: | Size: 627 B After Width: | Height: | Size: 627 B |
|
Before Width: | Height: | Size: 536 B After Width: | Height: | Size: 536 B |
|
Before Width: | Height: | Size: 685 B After Width: | Height: | Size: 685 B |
|
Before Width: | Height: | Size: 117 B After Width: | Height: | Size: 117 B |
|
Before Width: | Height: | Size: 352 B After Width: | Height: | Size: 352 B |
|
Before Width: | Height: | Size: 344 B After Width: | Height: | Size: 344 B |
|
Before Width: | Height: | Size: 211 B After Width: | Height: | Size: 211 B |