...
Some checks failed
Run Tests / unit-tests (pull_request) Has been cancelled
Run Tests / cypress-tests (chrome) (pull_request) Has been cancelled
Run Tests / cypress-tests (edge) (pull_request) Has been cancelled
Run Tests / cypress-tests (firefox) (pull_request) Has been cancelled
Run Tests / install (pull_request) Has been cancelled
Run Tests / install (push) Failing after 7m36s
Run Tests / unit-tests (push) Has been skipped
Run Tests / cypress-tests (chrome) (push) Has been skipped
Run Tests / cypress-tests (edge) (push) Has been skipped
Run Tests / cypress-tests (firefox) (push) Has been skipped

This commit is contained in:
Mrrp 2025-03-06 02:04:47 -08:00
parent 48a9d6649f
commit 39739d0444
12 changed files with 880 additions and 579 deletions

View file

@ -1,4 +1,4 @@
name: Testing
name: Run Tests
on:
push:
@ -8,21 +8,105 @@ on:
branches:
- '*'
env:
NODE_VERSION: 23
APT_PACKAGES: "xvfb libnss3 libatk-bridge2.0-0 libxkbcommon-x11-0 libgtk-3-0 libgbm1 libasound2 libxss1"
jobs:
test:
install:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v2
with:
node-version: '23'
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Cache APT dependencies
id: cache-apt
uses: actions/cache@v4
with:
path: /var/cache/apt/archives
key: apt-${{ runner.os }}-${{ env.APT_PACKAGES }}
- name: Run Unit tests
run: npm run test:unit
- name: Install APT dependencies (if cache miss)
if: steps.cache-apt.outputs.cache-hit != 'true'
run: sudo apt-get update && sudo apt-get install -y ${{ env.APT_PACKAGES }}
- name: Install NPM dependencies
run: npm ci
- name: Cache Cypress binary
uses: actions/cache@v4
with:
path: ~/.cache/Cypress
key: cypress-${{ runner.os }}-${{ env.NODE_VERSION }}-${{ hashFiles('package-lock.json') }}
- name: Install Cypress
run: npx cypress install
- name: Build project
run: npm run build
- name: Save build artifact
uses: actions/upload-artifact@v4
with:
name: build
path: dist
unit-tests:
runs-on: ubuntu-latest
needs: install
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Download build artifact
uses: actions/download-artifact@v4
with:
name: build
path: dist
- name: Run unit tests
run: npm run test:unit
cypress-tests:
runs-on: ubuntu-latest
needs: install
strategy:
fail-fast: false
matrix:
browser: [chrome, firefox, edge]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Download build artifact
uses: actions/download-artifact@v4
with:
name: build
path: dist
- name: Install APT dependencies
run: sudo apt-get update && sudo apt-get install -y ${{ env.APT_PACKAGES }}
- name: Cypress run (${{ matrix.browser }})
uses: cypress-io/github-action@v6
with:
start: npm start
browser: ${{ matrix.browser }}

View file

@ -1,8 +1,8 @@
// https://on.cypress.io/api
describe('My First Test', () => {
describe('Site Sanity Check', () => {
it('visits the app root url', () => {
cy.visit('/')
cy.contains('h1', 'You did it!')
cy.contains('h2', 'Development Projects')
})
})

757
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -3,19 +3,19 @@
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "run-p type-check \"build-only {@}\" --",
"preview": "vite preview",
"test:unit": "vitest --passWithNoTests",
"test:e2e": "start-server-and-test preview http://localhost:4173 'cypress run --e2e'",
"test:e2e:dev": "start-server-and-test 'vite dev --port 4173' http://localhost:4173 'cypress open --e2e'",
"build-only": "vite build",
"type-check": "vue-tsc --build --force",
"lint": "eslint . --fix",
"format": "prettier --write src/",
"start": "npm run dev --host"
},
"scripts": {
"dev": "vite",
"build": "run-p type-check \"build-only {@}\" --",
"preview": "vite preview",
"test:unit": "vitest --passWithNoTests",
"test:e2e": "start-server-and-test 'vite preview' http://localhost:4173 'cypress run --e2e'",
"test:e2e:dev": "start-server-and-test 'vite dev --port 4173' http://localhost:4173 'cypress open --e2e'",
"build-only": "vite build",
"type-check": "vue-tsc --build --force",
"lint": "eslint . --fix",
"format": "prettier --write src/",
"start": "npm run dev --host"
},
"dependencies": {
"@tsparticles/slim": "^3.5.0",
"@tsparticles/vue3": "^3.0.1",

View file

@ -1,35 +1,35 @@
<script setup lang="ts">
import { RouterLink, RouterView } from 'vue-router';
import { ref, onMounted } from 'vue';
import { RouterLink, RouterView } from 'vue-router'
import { ref, onMounted } from 'vue'
// Theme state and available themes
const theme = ref(localStorage.getItem("theme") || "mocha");
const theme = ref(localStorage.getItem('theme') || 'mocha')
const availableThemes = [
"mocha",
"latte",
"yule-night",
"yule-day",
"midsummer-twilight",
"midsummer-daylight",
"fireworks-night",
"parade-day",
"harvest-twilight",
"golden-hour",
"stargazer",
"daydreamer",
];
'mocha',
'latte',
'yule-night',
'yule-day',
'midsummer-twilight',
'midsummer-daylight',
'fireworks-night',
'parade-day',
'harvest-twilight',
'golden-hour',
'stargazer',
'daydreamer',
]
// Apply theme
onMounted(() => {
document.documentElement.setAttribute("data-theme", theme.value);
});
document.documentElement.setAttribute('data-theme', theme.value)
})
// Change theme function
const changeTheme = (newTheme: string) => {
theme.value = newTheme;
document.documentElement.setAttribute("data-theme", newTheme);
localStorage.setItem("theme", newTheme);
};
theme.value = newTheme
document.documentElement.setAttribute('data-theme', newTheme)
localStorage.setItem('theme', newTheme)
}
</script>
<template>
@ -48,11 +48,11 @@ const changeTheme = (newTheme: string) => {
</div>
<!-- Theme Selector -->
<div class="theme-selector">
<label for="theme-switcher">Theme:</label>
<select id="theme-switcher" v-model="theme" @change="changeTheme(theme)">
<option v-for="t in availableThemes" :key="t" :value="t">{{ t }}</option>
</select>
</div>
<label for="theme-switcher">Theme:</label>
<select id="theme-switcher" v-model="theme" @change="changeTheme(theme)">
<option v-for="t in availableThemes" :key="t" :value="t">{{ t }}</option>
</select>
</div>
</nav>
</header>

View file

@ -1,5 +1,5 @@
/* Catppuccin Mocha */
:root[data-theme="mocha"] {
:root[data-theme='mocha'] {
--color-surface0: #1e1e2e; /* Base */
--color-surface1: #313244; /* Mantle */
--color-surface2: #45475a; /* Surface */
@ -26,7 +26,7 @@
}
/* Catppuccin Latte */
:root[data-theme="latte"] {
:root[data-theme='latte'] {
--color-surface0: #eff1f5; /* Base */
--color-surface1: #e6e9ef; /* Mantle */
--color-surface2: #ccd0da; /* Surface */
@ -52,7 +52,7 @@
--color-overlay2: #9ca0b0;
}
:root[data-theme="yule-night"] {
:root[data-theme='yule-night'] {
--color-surface0: #1b1d28; /* Deep midnight */
--color-surface1: #252936; /* Frosty steel */
--color-surface2: #343a48; /* Snow shadow */
@ -62,7 +62,7 @@
--color-border: #475266; /* Frosty edges */
}
:root[data-theme="yule-day"] {
:root[data-theme='yule-day'] {
--color-surface0: #f5f3ed; /* Fresh snow */
--color-surface1: #ece7df; /* Frosty beige */
--color-surface2: #dcd3c3; /* Hearth ash */
@ -72,7 +72,7 @@
--color-border: #9d9684; /* Frosted wood */
}
:root[data-theme="midsummer-twilight"] {
:root[data-theme='midsummer-twilight'] {
--color-surface0: #241f36; /* Starry violet */
--color-surface1: #2e2746; /* Dusky purple */
--color-surface2: #403659; /* Twilight shadow */
@ -82,7 +82,7 @@
--color-border: #6b5a89; /* Lavender dusk */
}
:root[data-theme="midsummer-daylight"] {
:root[data-theme='midsummer-daylight'] {
--color-surface0: #faf8eb; /* Bright sunlight */
--color-surface1: #f2e7c4; /* Sunlit field */
--color-surface2: #e6d399; /* Wheat gold */
@ -92,7 +92,7 @@
--color-border: #a38a5b; /* Golden shadows */
}
:root[data-theme="fireworks-night"] {
:root[data-theme='fireworks-night'] {
--color-surface0: #0a0e1a; /* Starry sky */
--color-surface1: #121b32; /* Midnight blue */
--color-surface2: #1f2945; /* Smoke cloud */
@ -102,7 +102,7 @@
--color-border: #3b4e7e; /* Steel blue */
}
:root[data-theme="parade-day"] {
:root[data-theme='parade-day'] {
--color-surface0: #fafafa; /* White fabric */
--color-surface1: #eaeaea; /* Pale silver */
--color-surface2: #c9d3e3; /* Cerulean mist */
@ -112,7 +112,7 @@
--color-border: #8795b4; /* Cloud blue */
}
:root[data-theme="harvest-twilight"] {
:root[data-theme='harvest-twilight'] {
--color-surface0: #1d1b13; /* Shadowed wheat field */
--color-surface1: #29231a; /* Earthen soil */
--color-surface2: #4b3b27; /* Golden dusk */
@ -122,7 +122,7 @@
--color-border: #5d4633; /* Bark brown */
}
:root[data-theme="golden-hour"] {
:root[data-theme='golden-hour'] {
--color-surface0: #fef6e6; /* Golden wheat */
--color-surface1: #fdecc8; /* Honey glow */
--color-surface2: #fcd399; /* Pumpkin yellow */
@ -132,7 +132,7 @@
--color-border: #a88a5f; /* Field shadows */
}
:root[data-theme="stargazer"] {
:root[data-theme='stargazer'] {
--color-surface0: #0d1321; /* Midnight sky */
--color-surface1: #1c2533; /* Cloudy night */
--color-surface2: #283142; /* Subtle twilight */
@ -142,7 +142,7 @@
--color-border: #3e506a; /* Lunar blue */
}
:root[data-theme="daydreamer"] {
:root[data-theme='daydreamer'] {
--color-surface0: #f9f9fc; /* Light paper */
--color-surface1: #eceef3; /* Morning mist */
--color-surface2: #d7dcea; /* Overcast sky */
@ -151,4 +151,3 @@
--color-accent-hover: #81a1c1; /* Brighter sky blue */
--color-border: #b2c4d4; /* Subtle frost */
}

View file

@ -20,7 +20,9 @@ body {
a {
text-decoration: none;
color: var(--color-accent);
transition: color 0.3s, background-color 0.3s;
transition:
color 0.3s,
background-color 0.3s;
}
a:hover {
@ -48,7 +50,9 @@ header nav a {
padding: 0.5rem 1rem;
font-size: 1rem;
color: var(--color-accent);
transition: color 0.3s, background-color 0.3s;
transition:
color 0.3s,
background-color 0.3s;
}
header nav a:hover {
@ -104,7 +108,9 @@ header nav a.router-link-exact-active {
padding: 0.5rem 1rem;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s, color 0.3s;
transition:
background-color 0.3s,
color 0.3s;
}
.logout-button:hover {
@ -300,7 +306,9 @@ main {
padding: 0.5rem 1rem;
border-radius: 4px;
cursor: pointer;
transition: background 0.3s ease, transform 0.2s ease;
transition:
background 0.3s ease,
transform 0.2s ease;
}
.close-button:hover {
@ -330,7 +338,9 @@ main {
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
text-align: center;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
transition:
transform 0.2s,
box-shadow 0.2s;
}
.server-card:hover {
@ -429,7 +439,9 @@ main {
padding: 0.5rem 1rem;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s ease, transform 0.2s ease;
transition:
background-color 0.3s ease,
transform 0.2s ease;
}
.close-button:hover {
@ -459,7 +471,9 @@ main {
padding: 0.5rem 1rem;
border-radius: 4px;
cursor: pointer;
transition: background 0.3s, transform 0.2s ease;
transition:
background 0.3s,
transform 0.2s ease;
}
.faq-button:hover {
@ -601,7 +615,9 @@ main {
padding: 0.5rem 1rem;
border-radius: 4px;
cursor: pointer;
transition: background 0.3s ease, transform 0.2s ease;
transition:
background 0.3s ease,
transform 0.2s ease;
margin-top: 1rem;
}
@ -679,7 +695,9 @@ main {
border-radius: 4px;
cursor: pointer;
font-size: 1rem;
transition: background 0.3s, transform 0.2s;
transition:
background 0.3s,
transform 0.2s;
}
.login .btn-login:hover {

View file

@ -1,3 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@tailwind base;
@tailwind components;
@tailwind utilities;

View file

@ -1,21 +1,21 @@
import axios from "axios";
import axios from 'axios'
const API_BASE = "http://localhost:3000"; // Update with your backend address
const API_BASE = 'http://localhost:3000' // Update with your backend address
export const register = async (username: string, password: string) => {
return axios.post(`${API_BASE}/register`, { username, password });
};
return axios.post(`${API_BASE}/register`, { username, password })
}
export const login = async (username: string, password: string) => {
const response = await axios.post(`${API_BASE}/login`, { username, password });
const token = response.data.token;
localStorage.setItem("token", token); // Save token for authenticated requests
return token;
};
const response = await axios.post(`${API_BASE}/login`, { username, password })
const token = response.data.token
localStorage.setItem('token', token) // Save token for authenticated requests
return token
}
export const getProtectedData = async () => {
const token = localStorage.getItem("token");
const token = localStorage.getItem('token')
return axios.get(`${API_BASE}/protected`, {
headers: { Authorization: `Bearer ${token}` },
});
};
})
}

View file

@ -1,6 +1,5 @@
<template>
<div class="landing-page">
<section class="sections">
<!-- Family Content -->
<div class="section-box">
@ -11,7 +10,9 @@
<h3>Sharkey</h3>
<p>
Twitter alternative without a flood of Nazis.<br /><br />
We manually approve all members + verify them outside of the net, too. Fam & friends only to make an account in our space, but you can use any Sharkey/Mastodon/etc instance to register elsewhere and talk to us.
We manually approve all members + verify them outside of the net, too. Fam & friends
only to make an account in our space, but you can use any Sharkey/Mastodon/etc
instance to register elsewhere and talk to us.
</p>
<a href="https://social.smgames.club/" class="link">Go to Platform</a>
</div>
@ -19,8 +20,8 @@
<div class="card">
<h3>Game Servers</h3>
<p>
Privately hosted servers for Minecraft, Garry's Mod, TF2, Terraria, and more.
Clicking each game shows instructions to get on.
Privately hosted servers for Minecraft, Garry's Mod, TF2, Terraria, and more. Clicking
each game shows instructions to get on.
</p>
<a href="/servers" class="link">See Our Game Servers</a>
</div>

View file

@ -5,37 +5,51 @@
<!-- Overview Section -->
<div class="overview">
<p>
Welcome to our projects page! We create <strong>websites</strong>, <strong>video games</strong>,
<strong>addons for games</strong>, <strong>avatars and worlds for VRChat</strong>, and
<strong>game assets</strong>. Explore our ongoing projects below.
Welcome to our projects page! We create <strong>websites</strong>,
<strong>video games</strong>, <strong>addons for games</strong>,
<strong>avatars and worlds for VRChat</strong>, and <strong>game assets</strong>. Explore
our ongoing projects below.
</p>
<button @click="showFaqModal = true" class="faq-button">FAQ</button>
</div>
<!-- Filter Buttons -->
<div class="filter-bar">
<button v-for="tag in tags" :key="tag" @click="filterByTag(tag)" :class="{ active: selectedTag === tag }">
<button
v-for="tag in tags"
:key="tag"
@click="filterByTag(tag)"
:class="{ active: selectedTag === tag }"
>
{{ tag }}
</button>
</div>
<!-- Projects Grid -->
<div class="project-grid">
<div
v-for="project in filteredProjects"
:key="project.name"
class="project-card"
>
<div v-for="project in filteredProjects" :key="project.name" class="project-card">
<h3>{{ project.name }}</h3>
<p>{{ project.description }}</p>
<ul class="tags">
<li v-for="tag in project.tags" :key="tag" class="tag">{{ tag }}</li>
</ul>
<div class="links">
<p v-if="project.links.public"><strong>Public:</strong> <a :href="project.links.public" target="_blank">{{ project.links.public }}</a></p>
<p v-if="project.links.local"><strong>Local:</strong> <a :href="project.links.local" target="_blank">{{ project.links.local }}</a></p>
<p v-if="project.links.testing"><strong>Testing:</strong> <a :href="project.links.testing" target="_blank">{{ project.links.testing }}</a></p>
<p v-if="project.links.wiki"><strong>Wiki:</strong> <a :href="project.links.wiki" target="_blank">{{ project.links.wiki }}</a></p>
<p v-if="project.links.public">
<strong>Public:</strong>
<a :href="project.links.public" target="_blank">{{ project.links.public }}</a>
</p>
<p v-if="project.links.local">
<strong>Local:</strong>
<a :href="project.links.local" target="_blank">{{ project.links.local }}</a>
</p>
<p v-if="project.links.testing">
<strong>Testing:</strong>
<a :href="project.links.testing" target="_blank">{{ project.links.testing }}</a>
</p>
<p v-if="project.links.wiki">
<strong>Wiki:</strong>
<a :href="project.links.wiki" target="_blank">{{ project.links.wiki }}</a>
</p>
</div>
</div>
</div>
@ -44,58 +58,70 @@
<div v-if="showFaqModal" class="modal-overlay" @click="closeFaqModal">
<div class="modal-content" @click.stop>
<h2>Frequently Asked Questions</h2>
<hr>
<hr />
<ul class="faq-list">
<li>
<strong>"Do you need an idea person?"</strong>
<li>
<strong>"Do you need an idea person?"</strong>
<p>No. Not now, and likely not ever.</p>
</li>
<hr>
<hr />
<li>
<strong>"Do you need an asset creator? What would you pay me?"</strong>
<p>
This applies to graphics, models, sounds, music, and similar work. Suggesting random ideas and demanding payment isnt how this works.
Provide a portfolio and clear price sheets. We offer fair indie rates based on quality. If your work is lower quality, understand that others
may carry more of the creative load.
This applies to graphics, models, sounds, music, and similar work. Suggesting random
ideas and demanding payment isnt how this works. Provide a portfolio and clear price
sheets. We offer fair indie rates based on quality. If your work is lower quality,
understand that others may carry more of the creative load.
</p>
</li>
<hr>
<hr />
<li>
<strong>"If I'm hired to work on sound effects, that means I'm the sound director, right?"</strong>
<strong
>"If I'm hired to work on sound effects, that means I'm the sound director,
right?"</strong
>
<p>
No, it doesnt. Roles like "director" require experience, leadership skills, and a proven track record. Freelance roles are just thatfreelance.
Dont expect a salary or profit share from such a position. Additionally, if we hire you to create assets, <b>we</b> decide whats needed,
unless creative freedom is explicitly granted. Thank you for understanding.
No, it doesnt. Roles like "director" require experience, leadership skills, and a
proven track record. Freelance roles are just thatfreelance. Dont expect a salary or
profit share from such a position. Additionally, if we hire you to create assets,
<b>we</b> decide whats needed, unless creative freedom is explicitly granted. Thank
you for understanding.
</p>
</li>
<hr>
<hr />
<li>
<strong>"Well, how much WOULD you pay me?"</strong>
<p>
Provide your portfolio and rates. If you dont have one, well evaluate your work against indie standards to make a fair offer. We're not presently hiring.
Provide your portfolio and rates. If you dont have one, well evaluate your work
against indie standards to make a fair offer. We're not presently hiring.
</p>
</li>
<hr>
<hr />
<li>
<strong>On suggesting ideas and claiming ownership</strong>
<p>
Ideas, concepts, and words alone are not copyrightableonly tangible creations such as art, sound effects, or completed works can be protected by copyright.
Claiming ownership of an idea without contributing to its execution is not recognized in the professional world. That said, we value constructive suggestions and
may acknowledge meaningful contributions with in-game benefits or recognition. However, we maintain a zero-tolerance policy for unfounded claims or disruptive behavior,
to ensure a fair and respectful environment for everyone.
Ideas, concepts, and words alone are not copyrightableonly tangible creations such as
art, sound effects, or completed works can be protected by copyright. Claiming
ownership of an idea without contributing to its execution is not recognized in the
professional world. That said, we value constructive suggestions and may acknowledge
meaningful contributions with in-game benefits or recognition. However, we maintain a
zero-tolerance policy for unfounded claims or disruptive behavior, to ensure a fair
and respectful environment for everyone.
</p>
</li>
<hr>
<hr />
<li>
<strong>"You guys make games, so I want you to make this concept of mine!"</strong>
<p>
Not for free. We have our own projects to focus on, and <u>we are not for hire at this time</u>. Additionally, being an "idea person" is not a paid position in the game
development industry. Successful game creation requires collaboration, execution, and tangible contributions.
Not for free. We have our own projects to focus on, and
<u>we are not for hire at this time</u>. Additionally, being an "idea person" is not a
paid position in the game development industry. Successful game creation requires
collaboration, execution, and tangible contributions.
</p>
</li>
</ul>
<hr>
<hr />
<button @click="closeFaqModal" class="close-button">Close</button>
</div>
</div>
@ -107,71 +133,80 @@ export default {
data() {
return {
showFaqModal: false,
selectedTag: "All",
tags: ["All", "Website", "PC", "Singleplayer", "Multiplayer", "ALPHA", "Horror", "Relaxing", "Sim", "RPG"],
selectedTag: 'All',
tags: [
'All',
'Website',
'PC',
'Singleplayer',
'Multiplayer',
'ALPHA',
'Horror',
'Relaxing',
'Sim',
'RPG',
],
projects: [
{
name: "Wildspace",
description: "A browser pet game.",
tags: ["Website", "Multiplayer", "ALPHA"],
name: 'Wildspace',
description: 'A browser pet game.',
tags: ['Website', 'Multiplayer', 'ALPHA'],
links: {
public: "https://thewild.space",
public: 'https://thewild.space',
local: null,
testing: null,
wiki: "wiki.smgames.club/wildspace",
wiki: 'wiki.smgames.club/wildspace',
},
},
{
name: "Ghostbound",
description: "A ghost hunting game.",
tags: ["PC", "Singleplayer", "Multiplayer", "ALPHA", "Horror", "Team"],
name: 'Ghostbound',
description: 'A ghost hunting game.',
tags: ['PC', 'Singleplayer', 'Multiplayer', 'ALPHA', 'Horror', 'Team'],
links: {
public: null,
local: null,
testing: null,
wiki: "wiki.smgames.club/ghostbound",
wiki: 'wiki.smgames.club/ghostbound',
},
},
{
name: '"Island"',
description: "A cozy play-at-your-pace game.",
tags: ["PC", "Singleplayer", "Multiplayer", "ALPHA", "Relaxing", "Sim"],
description: 'A cozy play-at-your-pace game.',
tags: ['PC', 'Singleplayer', 'Multiplayer', 'ALPHA', 'Relaxing', 'Sim'],
links: {
public: null,
local: null,
testing: null,
wiki: "wiki.smgames.club/island",
wiki: 'wiki.smgames.club/island',
},
},
{
name: '"Random RPG"',
description: "An RPG game with actions and consequences.",
tags: ["PC", "Singleplayer", "ALPHA", "RPG"],
description: 'An RPG game with actions and consequences.',
tags: ['PC', 'Singleplayer', 'ALPHA', 'RPG'],
links: {
public: null,
local: null,
testing: null,
wiki: "wiki.smgames.club/randomrpg",
wiki: 'wiki.smgames.club/randomrpg',
},
},
],
};
}
},
computed: {
filteredProjects() {
if (this.selectedTag === "All") return this.projects;
return this.projects.filter((project) =>
project.tags.includes(this.selectedTag)
);
if (this.selectedTag === 'All') return this.projects
return this.projects.filter((project) => project.tags.includes(this.selectedTag))
},
},
methods: {
filterByTag(tag: string) {
this.selectedTag = tag;
this.selectedTag = tag
},
closeFaqModal() {
this.showFaqModal = false;
this.showFaqModal = false
},
},
};
}
</script>

View file

@ -13,7 +13,9 @@
@click="openModal(name)"
>
<h3>{{ name }}</h3>
<p>Status: <span :class="server.status">{{ server.status }}</span></p>
<p>
Status: <span :class="server.status">{{ server.status }}</span>
</p>
</div>
</div>
</section>
@ -29,7 +31,9 @@
@click="openModal(name)"
>
<h3>{{ name }}</h3>
<p>Status: <span :class="server.status">{{ server.status }}</span></p>
<p>
Status: <span :class="server.status">{{ server.status }}</span>
</p>
</div>
</div>
</section>
@ -39,10 +43,14 @@
<div class="modal-content" @click.stop>
<img v-if="modalData.banner" :src="modalData.banner" alt="Server Banner" class="banner" />
<h2>{{ modalData.name || 'No Server Selected' }}</h2>
<p>Status:
<p>
Status:
<span :class="modalData.status">
<i v-if="modalData.status === 'online'" class="fas fa-check-circle online-icon"></i>
<i v-else-if="modalData.status === 'offline'" class="fas fa-times-circle offline-icon"></i>
<i
v-else-if="modalData.status === 'offline'"
class="fas fa-times-circle offline-icon"
></i>
<i v-else class="fas fa-question-circle unknown-icon"></i>
{{ modalData.status || 'unknown' }}
</span>
@ -82,24 +90,24 @@
</template>
<script lang="ts">
import { defineComponent } from "vue";
import { defineComponent } from 'vue'
interface ServerInstructions {
public: string;
local: string;
public: string
local: string
}
interface Server {
name?: string;
banner: string;
status: string;
playersOnline?: number;
maxPlayers?: number;
about: string;
instructions: ServerInstructions;
installInstructions?: string;
queryIP: string;
link: string | null;
name?: string
banner: string
status: string
playersOnline?: number
maxPlayers?: number
about: string
instructions: ServerInstructions
installInstructions?: string
queryIP: string
link: string | null
}
export default defineComponent({
@ -109,207 +117,202 @@ export default defineComponent({
modalData: {} as Server,
alwaysOnServers: {
"Minecraft Java Modern": {
banner: "minecraft-modern-banner.jpg",
status: "unknown",
'Minecraft Java Modern': {
banner: 'minecraft-modern-banner.jpg',
status: 'unknown',
playersOnline: 0,
maxPlayers: 20,
about: "This is the modern Minecraft Java server.",
about: 'This is the modern Minecraft Java server.',
instructions: {
public: "",
local: "192.168.1.201",
public: '',
local: '192.168.1.201',
},
installInstructions: "Download Minecraft Java Edition from Mojang's website.",
queryIP: "",
link: "",
queryIP: '',
link: '',
},
"Minecraft Java 1.12.2": {
banner: "minecraft-1-12-banner.jpg",
status: "unknown",
'Minecraft Java 1.12.2': {
banner: 'minecraft-1-12-banner.jpg',
status: 'unknown',
playersOnline: 0,
maxPlayers: 10,
about: "This is the Minecraft Java 1.12.2 server with mods.",
about: 'This is the Minecraft Java 1.12.2 server with mods.',
instructions: {
public: "",
local: "",
public: '',
local: '',
},
installInstructions: "Install Forge 1.12.2 and download the modpack provided.",
queryIP: "",
link: "",
installInstructions: 'Install Forge 1.12.2 and download the modpack provided.',
queryIP: '',
link: '',
},
Terraria: {
banner: "terraria-banner.jpg",
status: "unknown",
banner: 'terraria-banner.jpg',
status: 'unknown',
playersOnline: 0,
maxPlayers: 8,
about: "Our modded Terraria instance for friends and family.",
about: 'Our modded Terraria instance for friends and family.',
instructions: {
public: "",
local: "192.168.1.121",
public: '',
local: '192.168.1.121',
},
installInstructions:
"Use TModLoader from Steam (not base Terraria) and download our pack.",
queryIP: "",
link: "https://steamcommunity.com/sharedfiles/filedetails/?id=2943030068",
'Use TModLoader from Steam (not base Terraria) and download our pack.',
queryIP: '',
link: 'https://steamcommunity.com/sharedfiles/filedetails/?id=2943030068',
},
"Team Fortress 2": {
banner: "tf2-banner.jpg",
status: "unknown",
'Team Fortress 2': {
banner: 'tf2-banner.jpg',
status: 'unknown',
playersOnline: 0,
maxPlayers: 16,
about: "Classic Team Fortress 2 fun with friends.",
about: 'Classic Team Fortress 2 fun with friends.',
instructions: {
public: "",
local: "192.168.1.203",
public: '',
local: '192.168.1.203',
},
installInstructions: "Download Team Fortress 2 for free on Steam.",
queryIP: "",
link: "",
installInstructions: 'Download Team Fortress 2 for free on Steam.',
queryIP: '',
link: '',
},
} as Record<string, Server>, // Add type Record<string, Server> for alwaysOnServers
toggledServers: {
"Core Keeper": {
banner: "core-keeper-banner.jpg",
status: "unknown",
'Core Keeper': {
banner: 'core-keeper-banner.jpg',
status: 'unknown',
playersOnline: 0,
maxPlayers: 16,
about: "Explore, mine, and survive in this pixelated adventure.",
about: 'Explore, mine, and survive in this pixelated adventure.',
instructions: {
public: "",
local: "192.168.1.204",
public: '',
local: '192.168.1.204',
},
installInstructions: "Available on Steam. Ensure your game is up to date.",
queryIP: "",
installInstructions: 'Available on Steam. Ensure your game is up to date.',
queryIP: '',
link: null,
},
ECO: {
banner: "eco-banner.jpg",
status: "unknown",
banner: 'eco-banner.jpg',
status: 'unknown',
playersOnline: 0,
maxPlayers: 50,
about: "A global survival game where players build an ecosystem.",
about: 'A global survival game where players build an ecosystem.',
instructions: {
public: "eco.example.com",
local: "",
public: 'eco.example.com',
local: '',
},
installInstructions:
"Download ECO from the official website or Steam. Use the provided IP.",
queryIP: "http://eco.example.com/status",
'Download ECO from the official website or Steam. Use the provided IP.',
queryIP: 'http://eco.example.com/status',
link: null,
},
Enshrouded: {
banner: "enshrouded-banner.jpg",
status: "unknown",
banner: 'enshrouded-banner.jpg',
status: 'unknown',
playersOnline: 0,
maxPlayers: 16,
about: "A survival crafting game in a mysterious fantasy setting.",
about: 'A survival crafting game in a mysterious fantasy setting.',
instructions: {
public: "",
local: "",
public: '',
local: '',
},
installInstructions: undefined,
queryIP: "http://enshrouded.example.com/status",
queryIP: 'http://enshrouded.example.com/status',
link: null,
},
Empyrion: {
banner: "empyrion-banner.jpg",
status: "unknown",
banner: 'empyrion-banner.jpg',
status: 'unknown',
playersOnline: 0,
maxPlayers: 16,
about: "Explore space and build your intergalactic empire.",
about: 'Explore space and build your intergalactic empire.',
instructions: {
public: "",
local: "",
public: '',
local: '',
},
installInstructions:
"Download Empyrion from Steam and ensure mods match server settings.",
queryIP: "http://empyrion.example.com/status",
'Download Empyrion from Steam and ensure mods match server settings.',
queryIP: 'http://empyrion.example.com/status',
link: null,
},
Palworld: {
banner: "palworld-banner.jpg",
status: "unknown",
banner: 'palworld-banner.jpg',
status: 'unknown',
playersOnline: 0,
maxPlayers: 32,
about: "A multiplayer game where you befriend and fight alongside creatures.",
about: 'A multiplayer game where you befriend and fight alongside creatures.',
instructions: {
public: "",
local: "",
public: '',
local: '',
},
installInstructions: "Available on Steam. Join the server via multiplayer menu.",
queryIP: "http://palworld.example.com/status",
installInstructions: 'Available on Steam. Join the server via multiplayer menu.',
queryIP: 'http://palworld.example.com/status',
link: null,
},
"Survive The Nights": {
banner: "survive-the-nights-banner.jpg",
status: "unknown",
'Survive The Nights': {
banner: 'survive-the-nights-banner.jpg',
status: 'unknown',
playersOnline: 0,
maxPlayers: 10,
about: "A survival horror game set in a post-apocalyptic world.",
about: 'A survival horror game set in a post-apocalyptic world.',
instructions: {
public: "",
local: "",
public: '',
local: '',
},
installInstructions: "Purchase on Steam and ensure to update your client.",
queryIP: "http://survivethenights.example.com/status",
installInstructions: 'Purchase on Steam and ensure to update your client.',
queryIP: 'http://survivethenights.example.com/status',
link: null,
},
Valheim: {
banner: "valheim-banner.jpg",
status: "unknown",
banner: 'valheim-banner.jpg',
status: 'unknown',
playersOnline: 0,
maxPlayers: 10,
about: "A Viking-themed survival game set in a procedurally generated world.",
about: 'A Viking-themed survival game set in a procedurally generated world.',
instructions: {
public: "",
local: "",
public: '',
local: '',
},
installInstructions: "Install via Steam and ensure mods match server settings.",
queryIP: "http://valheim.example.com/status",
installInstructions: 'Install via Steam and ensure mods match server settings.',
queryIP: 'http://valheim.example.com/status',
link: null,
},
"V Rising": {
banner: "v-rising-banner.jpg",
status: "unknown",
'V Rising': {
banner: 'v-rising-banner.jpg',
status: 'unknown',
playersOnline: 0,
maxPlayers: 20,
about: "Rise as a vampire lord in this survival RPG.",
about: 'Rise as a vampire lord in this survival RPG.',
instructions: {
public: "",
local: "",
public: '',
local: '',
},
installInstructions:
"Available on Steam. Ensure your game and server mods are synced.",
queryIP: "http://vrising.example.com/status",
installInstructions: 'Available on Steam. Ensure your game and server mods are synced.',
queryIP: 'http://vrising.example.com/status',
link: null,
},
} as Record<string, Server>, // Add type Record<string, Server> for toggledServers
};
}
},
computed: {
showPlayerCount(): boolean {
return (
this.modalData.playersOnline !== undefined &&
this.modalData.maxPlayers !== undefined
);
return this.modalData.playersOnline !== undefined && this.modalData.maxPlayers !== undefined
},
displayPlayerCount(): string {
if (this.modalData.maxPlayers === undefined) return "? / ?";
if (this.modalData.playersOnline === undefined) return "? / ?";
return `${this.modalData.playersOnline} / ${this.modalData.maxPlayers}`;
if (this.modalData.maxPlayers === undefined) return '? / ?'
if (this.modalData.playersOnline === undefined) return '? / ?'
return `${this.modalData.playersOnline} / ${this.modalData.maxPlayers}`
},
},
methods: {
openModal(serverName: string) {
this.modalData =
this.alwaysOnServers[serverName] || this.toggledServers[serverName] || {};
this.modalData.name = serverName;
this.showModal = true;
this.modalData = this.alwaysOnServers[serverName] || this.toggledServers[serverName] || {}
this.modalData.name = serverName
this.showModal = true
},
closeModal() {
this.showModal = false;
this.showModal = false
},
},
});
})
</script>