Informacje ogólne
Platforma mini gier BitFun
Written By Coder Matthew
Last updated 8 months ago
1) Wprowadzenie
Platforma BitFun Games to dynamiczny serwis internetowy stworzony dla entuzjastów gier casualowych i zręcznościowych. Służy jako centralny katalog gier, umożliwiając użytkownikom rywalizację i śledzenie swoich postępów w rankingach globalnych.
Cały projekt utrzymany jest w nowoczesnej, ciemnej estetyce z dominującymi akcentami neonowo-cyjanowymi, co zapewnia spójne i atrakcyjne doświadczenie wizualne.
Czym jest platforma i jakie ma opcje?
Katalog Minigier: BitFun oferuje zestaw wciągających gier WebGL/Unity, takich jak Flappy Bird, Dino Game, Tetris czy Pack-Man, dostępnych do natychmiastowego uruchomienia.
Rywalizacja (Leaderboard): Zintegrowany system wyników i rankingów (Leaderboard) pozwala graczom śledzić swoje osiągnięcia i porównywać je z innymi użytkownikami w czasie rzeczywistym.
System Kont Użytkowników: Pełna obsługa rejestracji, logowania oraz zarządzania profilem. Konta są chronione silnym haszowaniem haseł (ARGON2ID) i wymagają weryfikacji e-mail.
Wsparcie Techniczne: Dedykowany system zgłoszeń (Ticket System) umożliwia zalogowanym użytkownikom szybkie kontaktowanie się z administracją i śledzenie statusu swoich problemów.
2) Przewodnik
2.1) Strona Główna

Na stronie głównej znajduje się katalog dostępnych gier w formie interaktywnych kafelków. Niektóre gry mogą być tymczasowo niedostępne.
2.2) Logowanie i Rejestracja

Platforma wymaga konta do pełnego korzystania z funkcji rankingowych i wsparcia. Rejestracja jest zabezpieczona przez Google reCAPTCHA.
2.3) Panel Użytkownika i Osiągnięcia

W panelu użytkownika można znaleźć szczegóły konta oraz sekcję Osiągnięcia, która wyświetla najlepsze wyniki gracza w poszczególnych grach. Użytkownik ma również opcję zmiany hasła.
2.4) Gra i Ranking

Po wybraniu gry, jest ona ładowana w widoku gry za pomocą technologii Unity/WebGL. Pod oknem gry znajduje się opcja Fullscreen oraz przyciski Resetuj i Pomoc. Pod grą, jeśli gra obsługuje ranking, wyświetlana jest automatycznie aktualizowana tabela Najlepszych Wyników (Top 10).
2.5) Wsparcie techniczne

Użytkownik może wypełnić formularz kontaktowy (Temat i Wiadomość). Po wysłaniu, zgłoszenie przechodzi do historii zgłoszeń wraz z statusem oczekującego.
2.6) Zmiana hasła

Dostępna jest możliwość zmiany hasła bez kontaktu z wsparciem, w tym celu należy przejść do ustawień a następnie usupełnić stare hasło, nowe oraz ponownie nowe.
2.7) Aktywacja konta

Rejestracja nowego konta wymaga jego aktywacji, w tym celu nalerzy przejść do klienta poczty email odszukać mail od Zespołu BitFun a następnie kliknąć Aktywuj Konto
3) Kod źródłowy
3.1) Katalog index
PHP:
3.2) Katalog scripts
CSS:
Example:root {
--neon-cyan: #00f5d4;
--dark-bg: #1a1a2e;
--tile-bg: #1f2833;
--text-color: #e0e0e0;
--border-color: #45a29e;
}
html, body {
background-color: var(--dark-bg);
font-family: 'Rajdhani', sans-serif;
color: var(--text-color);
}
.title, .subtitle, .button, .navbar-item, .label {
font-family: 'Rajdhani', sans-serif;
}
.button.is-primary {
background-color: var(--neon-cyan);
color: var(--dark-bg);
font-weight: 700;
border: none;
}
.button.is-primary:hover {
background-color: #ffffff;
color: var(--dark-bg);
box-shadow: 0 0 15px var(--neon-cyan);
}
.box.form-box {
background-color: var(--tile-bg);
border: 1px solid var(--border-color);
}
.form-box .label {
color: var(--text-color);
}
.form-box .input {
background-color: var(--dark-bg);
border-color: var(--border-color);
color: var(--text-color);
}
.form-box .input::placeholder {
color: #777;
}
.neon-title {
color: var(--neon-cyan);
border-bottom: 2px solid var(--neon-cyan);
padding-bottom: 10px;
margin-bottom: 30px;
}PHP:
Example<?php
$db = new mysqli("localhost","matthew","","matthew_bitfun");
?>JS:
Exampledocument.addEventListener('DOMContentLoaded', () => {
const loginForm = document.getElementById('login-form');
const registerForm = document.getElementById('register-form');
const displayMessage = (form, message, type) => {
const messageBox = form.querySelector('#message-box');
if (!messageBox) return;
const messageClass = type === 'success' ? 'notification is-success' : 'notification is-danger';
messageBox.innerHTML = `<div class="${messageClass} is-light">${message}</div>`;
};
const clearMessage = (form) => {
const messageBox = form.querySelector('#message-box');
if (messageBox) {
messageBox.innerHTML = '';
}
};
if (registerForm) {
registerForm.addEventListener('submit', async (e) => {
e.preventDefault();
clearMessage(registerForm);
const registerButton = document.getElementById('register-btn');
registerButton.classList.add('is-loading');
const formData = new FormData(registerForm);
try {
const response = await fetch('execs/exec_register.php', {
method: 'POST',
body: formData
});
const data = await response.json();
if (data.success) {
displayMessage(registerForm, data.message, 'success');
registerForm.reset();
grecaptcha.reset();
} else {
displayMessage(registerForm, data.message, 'error');
grecaptcha.reset();
}
} catch (error) {
displayMessage(registerForm, 'Wystąpił błąd sieci. Spróbuj ponownie.', 'error');
grecaptcha.reset();
}
registerButton.classList.remove('is-loading');
});
}
const supportForm = document.getElementById('support-form');
if (supportForm) {
supportForm.addEventListener('submit', async (e) => {
e.preventDefault();
clearMessage(supportForm);
const supportButton = document.getElementById('support-btn');
supportButton.classList.add('is-loading');
const formData = new FormData(supportForm);
try {
const response = await fetch('execs/exec_support.php', {
method: 'POST',
body: formData
});
const data = await response.json();
if (data.success) {
displayMessage(supportForm, data.message, 'success');
supportForm.reset();
setTimeout(() => {
window.location.reload();
}, 2000);
} else {
displayMessage(supportForm, data.message, 'error');
}
} catch (error) {
displayMessage(supportForm, 'Wystąpił błąd sieci. Spróbuj ponownie.', 'error');
}
supportButton.classList.remove('is-loading');
});
}
if (loginForm) {
loginForm.addEventListener('submit', async (e) => {
e.preventDefault();
clearMessage(loginForm);
const loginButton = document.getElementById('login-btn');
loginButton.classList.add('is-loading');
const formData = new FormData(loginForm);
try {
const response = await fetch('execs/exec_login.php', {
method: 'POST',
body: formData
});
const data = await response.json();
if (data.success) {
displayMessage(loginForm, data.message, 'success');
window.location.href = 'index.php';
} else {
displayMessage(loginForm, data.message, 'error');
}
} catch (error) {
displayMessage(loginForm, 'Wystąpił błąd sieci. Spróbuj ponownie.', 'error');
}
loginButton.classList.remove('is-loading');
});
}
});3.3) Katalog modules
PHP:
3.4) Katalog execs
PHP:
3.5) Katalog execs/api
PHP:
Example<?php
require_once("../../scripts/db.php");
header("Content-Type: application/json");
if (!isset($_GET['game_id'])) {
echo json_encode(["status" => "error", "message" => "Brak game_id"]);
exit;
}
$game_id = intval($_GET['game_id']);
$sql = "SELECT r.score, r.date, u.nick
FROM records r
JOIN users u ON r.user_id = u.id
WHERE r.game_id = ?
ORDER BY r.score DESC
LIMIT 10";
$stmt = $db->prepare($sql);
$stmt->bind_param("i", $game_id);
$stmt->execute();
$res = $stmt->get_result();
$data = [];
$pos = 0;
while ($row = $res->fetch_assoc()) {
$pos++;
$date = new DateTime($row['date']);
$newDate = $date->format('Y-m-d');
$data[] = [
"pos" => $pos,
"nick" => $row["nick"],
"score" => $row["score"],
"date" => $newDate
];
}
echo json_encode(["status" => "ok", "rows" => $data]);
3.6) Katalog errors
PHP
3.7) Baza danych
SQL:
Example-- phpMyAdmin SQL Dump
-- version 5.1.1deb5ubuntu1
-- https://www.phpmyadmin.net/
--
-- Host: localhost:3306
-- Czas generowania: 30 Lis 2025, 17:47
-- Wersja serwera: 8.0.44-0ubuntu0.22.04.1
-- Wersja PHP: 8.1.2-1ubuntu2.22
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
START TRANSACTION;
SET time_zone = "+00:00";
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
--
-- Baza danych: `matthew_bitfun`
--
-- --------------------------------------------------------
--
-- Struktura tabeli dla tabeli `games`
--
CREATE TABLE `games` (
`id` int NOT NULL,
`path` text COLLATE utf8mb4_polish_ci NOT NULL,
`img` text COLLATE utf8mb4_polish_ci NOT NULL,
`title` varchar(200) COLLATE utf8mb4_polish_ci NOT NULL,
`descr` text COLLATE utf8mb4_polish_ci NOT NULL,
`av` tinyint(1) NOT NULL,
`score` tinyint(1) NOT NULL,
`date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`author_id` int NOT NULL,
`rate` int NOT NULL DEFAULT '5'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_polish_ci;
--
-- Zrzut danych tabeli `games`
--
INSERT INTO `games` (`id`, `path`, `img`, `title`, `descr`, `av`, `score`, `date`, `author_id`, `rate`) VALUES
(3, '--', 'imgs/1.png', 'Snake', 'Zjedz wszystko, nie ugryź siebie! Uważaj na ściany!', 1, 1, '2025-11-14 12:18:47', 1, 5),
(4, 'games/SlotMachine/Build/SlotMachine', 'imgs/2.png', 'Slot machine', 'Prosta gra, wielka wygrana! Nie czekaj na szczęście!', 1, 0, '2025-11-14 12:18:47', 16, 5),
(5, '--', 'imgs/TimberMan.png', 'Timber Man', 'Rąb bez końca, nie zmiażdż się gałęzią! Uważaj na czas!', 0, 0, '2025-11-14 12:20:00', 1, 5),
(6, '--', 'imgs/Gta6.png', 'Grand Theft Auto VI', 'Uzbieraj fortunę, nie daj się złapać policji! Uważaj na sześć gwiazdek!', 0, 0, '2025-11-29 18:32:35', 18, 5),
(14, 'games/FlappyBird/Build/FlappyBird', 'imgs/FlappyBird.png', 'Flappy Bird', 'Utrzymaj się w powietrzu, nie dotknij rur! Uważaj na grawitację!', 1, 1, '2025-11-28 23:58:27', 18, 5),
(15, 'games/DinoGame/Build/DinoGame', 'imgs/DinoGame.png', 'Dino Game', 'Przeskakuj kaktusy, nie wpadnij na pterodaktyla! Uważaj na prędkość!', 1, 1, '2025-11-29 01:40:24', 18, 5),
(16, 'games/GeometryDash/Build/GeometryDash', 'imgs/GeometryDash.png', 'Geometry Dash', 'Skończ poziom, nie rozbij kostki! Uważaj na rytm!', 1, 1, '2025-11-29 01:43:22', 16, 5),
(17, 'games/RacingGame/Build/RacingGame', 'imgs/RacingGame.png', 'Racing Game', 'Ukończ wyścig, nie wpadnij na rywali! Uważaj na zakręty!', 1, 0, '2025-11-29 02:50:13', 16, 5),
(18, 'games/FruitNinja/Build/FruitNinja', 'imgs/FruitNinja.png', 'Fruit Ninja', 'Pokrój owoc, nie dotknij bomby! Uważaj na palce!', 1, 1, '2025-11-29 03:11:19', 18, 5),
(19, 'games/PackMan/Build/PackMan', 'imgs/PackMan.png', 'Pack-Man', 'Zjedz wszystkie kropki, nie daj się złapać duchom! Uważaj na drodze!', 1, 1, '2025-11-29 18:25:04', 18, 5),
(20, 'games/Tetris/Build/Tetris', 'imgs/Tetris.png', 'Tetris', 'Wypełnij linię, nie zostaw dziury! Uważaj na wysokość!', 1, 1, '2025-11-29 20:46:21', 18, 5);
-- --------------------------------------------------------
--
-- Struktura tabeli dla tabeli `records`
--
CREATE TABLE `records` (
`game_id` int NOT NULL,
`user_id` int NOT NULL,
`score` int NOT NULL,
`date` timestamp NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_polish_ci;
--
-- Zrzut danych tabeli `records`
--
INSERT INTO `records` (`game_id`, `user_id`, `score`, `date`) VALUES
(3, 16, 500, '2025-11-20 11:07:05'),
(14, 16, 19, '2025-11-29 00:52:56'),
(14, 18, 103, '2025-11-29 01:47:04'),
(15, 16, 116, '2025-11-29 02:45:03'),
(15, 18, 877, '2025-11-29 02:16:18'),
(18, 16, 48, '2025-11-29 23:59:41'),
(18, 18, 23, '2025-11-29 03:16:03'),
(19, 18, 12810, '2025-11-29 18:29:07'),
(19, 19, 4270, '2025-11-29 23:32:04'),
(20, 18, 200, '2025-11-29 21:08:15');
-- --------------------------------------------------------
--
-- Struktura tabeli dla tabeli `support`
--
CREATE TABLE `support` (
`id` int NOT NULL,
`user_id` int NOT NULL,
`subject` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`message` text COLLATE utf8mb4_unicode_ci NOT NULL,
`ip_address` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`status` enum('open','answered','closed') COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'open',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
-- Zrzut danych tabeli `support`
--
INSERT INTO `support` (`id`, `user_id`, `subject`, `message`, `ip_address`, `status`, `created_at`, `updated_at`) VALUES
(1, 16, 'hello', 'world<h1>Test</h1>', '192.168.1.166', 'open', '2025-11-17 17:04:46', '2025-11-17 17:04:46'),
(2, 16, 'fddfggfd', 'fdggdffdggfdfgd', '192.168.1.166', 'answered', '2025-11-17 17:05:01', '2025-11-17 17:05:38'),
(3, 16, 'hjhjkhjkk', 'hjkjkhhjkhjkhjk', '192.168.1.166', 'closed', '2025-11-17 17:05:08', '2025-11-17 17:05:51'),
(4, 16, 'sdfdfsfsd', 'dsfdsfsfsdfsd', '192.168.1.166', 'open', '2025-11-17 17:10:48', '2025-11-17 17:10:48'),
(5, 16, 'fdgfddfgfdg', 'dfgfdgfgdfgdfdgdfg', '192.168.1.166', 'open', '2025-11-17 17:11:42', '2025-11-17 17:11:42');
-- --------------------------------------------------------
--
-- Struktura tabeli dla tabeli `users`
--
CREATE TABLE `users` (
`id` int NOT NULL,
`nick` varchar(40) COLLATE utf8mb4_polish_ci NOT NULL,
`mail` varchar(120) COLLATE utf8mb4_polish_ci NOT NULL,
`pass` varchar(300) COLLATE utf8mb4_polish_ci NOT NULL,
`verified` tinyint(1) NOT NULL DEFAULT '0',
`verified_code` varchar(200) COLLATE utf8mb4_polish_ci NOT NULL,
`date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`ip` varchar(20) COLLATE utf8mb4_polish_ci NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_polish_ci;
--
-- Zrzut danych tabeli `users`
--
INSERT INTO `users` (`id`, `nick`, `mail`, `pass`, `verified`, `verified_code`, `date`, `ip`) VALUES
(16, 'Hekki', 'djprzerubka@gmail.com', '$argon2id$v=19$m=65536,t=4,p=1$RlhmZjJ0WU1DMGxQYUtGcg$Fs0POyPnLcRgZj4vuPhood9RApStzrixKKnUI0CaA+A', 1, '403a148a713bab3c9bed8666ba683b74e1431e4541', '2025-11-17 17:03:14', '192.168.1.166'),
(18, 'Michal White', 'xmilaxer@gmail.com', '$argon2id$v=19$m=65536,t=4,p=1$aFBrZ25CTjBrSnVUYkM1bw$dPNZGFSeFIgD17rnU0RPVJhFXjEHUOfGq+xrp1mLols', 1, '800a09e586a7576fca0f8d7bf090e129cdbf20868c', '2025-11-28 23:57:08', '192.168.1.166');
--
-- Indeksy dla zrzutów tabel
--
--
-- Indeksy dla tabeli `games`
--
ALTER TABLE `games`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `title` (`title`);
--
-- Indeksy dla tabeli `records`
--
ALTER TABLE `records`
ADD UNIQUE KEY `game_id` (`game_id`,`user_id`);
--
-- Indeksy dla tabeli `support`
--
ALTER TABLE `support`
ADD PRIMARY KEY (`id`),
ADD KEY `fk_support_user_idx` (`user_id`);
--
-- Indeksy dla tabeli `users`
--
ALTER TABLE `users`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `nick` (`nick`),
ADD UNIQUE KEY `verified_code` (`verified_code`);
--
-- AUTO_INCREMENT dla zrzuconych tabel
--
--
-- AUTO_INCREMENT dla tabeli `games`
--
ALTER TABLE `games`
MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=21;
--
-- AUTO_INCREMENT dla tabeli `support`
--
ALTER TABLE `support`
MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=6;
--
-- AUTO_INCREMENT dla tabeli `users`
--
ALTER TABLE `users`
MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=21;
--
-- Ograniczenia dla zrzutów tabel
--
--
-- Ograniczenia dla tabeli `support`
--
ALTER TABLE `support`
ADD CONSTRAINT `fk_support_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE;
COMMIT;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
4) Autorzy
Platforma BitFun Games została stworzona przez:
Mateusz Król
Michał Biały
Projekt zrealizowany na zlecenie: Zespół Szkół Technicznych i Ogólnokształcących im. Stefana Banacha w Jarosławiu
Całkowita liczba kodu platformy: 4477
Całkowita liczba kodu gier: 3271
Całkowita liczba stron dokumentacji: 160