Warehouse Management System
WIP
(2026)
C#
ASP.NET
EF Core
Fluent Validation
Fluent Assertions
Scrutor
Serilog
PostgreSQL
Docker
Azure
TypeScript
Vue.JS
Pinia
TanStack Query
VeeValidate
Zod
PrimeVue
Tailwind CSS
Chart.js
ApexCharts

System zarządzania magazynem obejmujący kompletny cykl życia przyjęcie → składowanie → wydanie: dane podstawowe produktów i lokalizacji, śledzenie partii/dat ważności, wielowymiarową pojemność lokalizacji, planowane odkładanie i kompletację, niezmienialny rejestr ruchów magazynowych oraz pulpit analityczny.

Backend to interfejs Web API oparty na ASP.NET Core (.NET 10), zbudowany w czystej, warstwowej architekturze. Frontend to aplikacja jednostronicowa (SPA) we Vue 3. Dane są przechowywane w PostgreSQL.


Live demo:   https://ambitious-wave-066504303.7.azurestaticapps.net/
Login: demo@wms.local
Hasło: 1wFWvd8zrS7!

Dane podstawowe
  • Produkty — SKU, opis, kategoria, wymagana strefa temperaturowa, masa/objętość jednostkowa (używane do obliczania obciążenia lokalizacji).
  • Kategorie produktów — hierarchiczne drzewo (rodzic/dziecko), z możliwością przeglądania i zmiany kolejności.
  • Lokalizacje — kod + ustrukturyzowany adres, typ (Storage / Quarantine / Returns), strefa temperaturowa (Ambient / Chilled / Frozen), wielowymiarowa pojemność (jednostki / masa / objętość — każda ograniczana niezależnie, decyduje najbardziej restrykcyjna), reguły mieszania SKU / mieszania partii, stany aktywny i zablokowany oraz preferowane produkty dla danej lokalizacji.
  • Partie — numer partii i data ważności na potrzeby śledzenia partii/dat ważności.
Inwentarz
  • Ilości na stanie / zarezerwowane / dostępne śledzone według lokalizacji + produktu + partii.
  • Ręczne korekty zapasów (ze ścieżką audytu) oraz wyszukiwanie dostępności.
Przyjęcie — Stock-In (przyjmowanie i odkładanie)
  • Cykl życia: Draft → Putaway → Completed (możliwość anulowania ze stanu Draft lub Putaway).
  • System automatycznie planuje rozmieszczenie podczas odkładania za pomocą planera odkładania. Strategie to: PreferredLocation, ConsolidateSameLot, ConsolidateSameSku, Proximity, NearestEmpty, NearestAvailable.
  • Ręczne nadpisanie lub ponowne zaplanowanie rozmieszczeń w stanie Draft.
  • Pojemność jest rezerwowana w momencie rozpoczęcia odkładania (z blokadą wiersza), aby równoległe przyjęcia nie przekroczyły dostępności lokalizacji.
  • Potwierdzanie odkładania pozycja po pozycji; anulowanie zwalnia rezerwacje i wycofuje już rozmieszczony zapas.
Wydanie — Stock-Out (kompletacja)
  • Cykl życia: Draft → Picking → Completed (z możliwością anulowania).
  • System planuje przydziały kompletacji w lokalizacjach/partiach za pomocą planera kompletacji. Strategie to: FEFO (najpierw najkrótszy termin ważności), FIFO, LIFO, LeastQuantity oraz nadpisanie Manual.
  • Edycja lokalizacji kompletacji / ponowne planowanie w stanie Draft; potwierdzanie kompletacji pozycja po pozycji; anulowanie zwraca zarezerwowany zapas.
Przesunięcia magazynowe i rejestr ruchów
  • Przemieszczanie zapasu między lokalizacjami.
  • Każda zmiana ilości (przyjęcie, wydanie, przesunięcie, korekta oraz ich anulowania) zapisuje niezmienialny rekord StockMovement, tworzony jako efekt uboczny poprzez zdarzenia domenowe — kompletna, tylko dopisywalna ścieżka audytu.
Pulpit i administracja
  • Pulpit z zakładkami: przegląd, przyjęcia, wydania, zapasy i pojemność (wykresy poprzez Chart.js / ApexCharts).
  • Uwierzytelnianie i role: logowanie JWT z trzema rolami — Admin, Manager, Worker. Wszystkie punkty końcowe domyślnie wymagają uwierzytelnienia; operacje zapisu wymagają roli Admin/Manager.
  • Panel administracyjny: zarządzanie użytkownikami (tworzenie / lista / usuwanie użytkowników, zmiana haseł) oraz narzędzia bazodanowe (zasilenie danymi demonstracyjnymi / wyczyszczenie).
Job Offer Aggregator
(2026)
Kod źródłowy
Python
FastAPI
SQLAlchemy
Pydantic
Playwright
BeautifulSoup
SQLite
TypeScript
Vue.JS
PrimeVue
Docker
OVHCloud

Agregator ogłoszeń o pracę w Belgii: zestaw scraperów zbiera oferty z pięciu portali, zapisuje je do wspólnej bazy z deduplikacją i udostępnia w jednym panelu z filtrami. Przebiegi startują automatycznie według harmonogramu ustawianego z poziomu aplikacji albo ręcznie, jednym przyciskiem.

Projekt zrealizowany na zlecenie firmy — wdrażany na serwerze klienta (w chmurze OVHCloud) jako zestaw kontenerów Docker.

Backend to FastAPI (SQLAlchemy + Pydantic, SQLite, migracje Alembic). Frontend to aplikacja jednostronicowa (SPA) we Vue 3 z TypeScriptem i PrimeVue.


Źródła ofert
  • Jooble — oficjalne API.
  • VDAB (belgijski urząd pracy) — pobieranie przez Playwright.
  • poloniusz.pl, axintor.be, impact.be — parsowanie listingów HTML (requests + BeautifulSoup).
  • Każde źródło ma własny scraper o wspólnym interfejsie; scrapery nie dotykają bazy — zwracają zwalidowane oferty, a zapisem i deduplikacją zajmuje się runner.
  • Deduplikacja po kluczu źródło:identyfikator z indeksem UNIQUE — powtórne przebiegi dopisują wyłącznie nowe ogłoszenia.
  • Błąd jednego scrapera nie przerywa pozostałych; pełny traceback trafia do historii uruchomień.
Panel
  • Oferty — tabela z paginacją po stronie serwera, sortowaniem i filtrami (źródło, stanowisko, lokalizacja, firma, fragment tytułu, data publikacji); nowe ogłoszenia są oznaczane.
  • Historia — przebiegi scraperów pogrupowane we wsady, ze statusem, liczbą znalezionych i nowych ofert oraz podglądem błędu.
  • Stanowiska — słownik wyszukiwanych stanowisk (nazwa PL/NL/FR + adresy listingów dla poszczególnych portali), edytowalny z poziomu panelu.
  • Harmonogram — godziny automatycznych przebiegów dodawane i włączane bez zmian w konfiguracji serwera.
Uruchamianie i wdrożenie
  • Osobna usługa harmonogramu nie scrapuje sama — o wyznaczonej godzinie wywołuje API, dzięki czemu automat i przycisk w panelu dzielą jedną blokadę i przebiegi nigdy się nie nakładają.
  • Logowanie hasłem, tokeny JWT; API nie jest wystawione na zewnątrz — ruch obsługuje nginx serwujący panel i przekazujący żądania do backendu.
  • Całość startuje jednym poleceniem docker compose up; baza i słownik stanowisk żyją w wolumenie, więc przebudowa obrazów ich nie narusza. Schemat bazy tworzą migracje wykonywane przy starcie.
Portfolio Template
(2026)
TypeScript
Angular
RxJS
SCSS
Bootstrap
GLightbox
JSON
Markdown

Szablon strony portfolio zbudowany w oparciu o framework Angular. Aplikacja nie posiada backendu — cała treść pochodzi z plików w katalogu public/assets, więc stworzenie własnego portfolio sprowadza się do ich podmiany, bez ingerencji w kod źródłowy. Całość budowana jest jako strona statyczna (SSG): każda podstrona, w każdej wersji językowej, trafia do gotowego HTML-a w czasie budowania i może być serwowana z CDN-u, bez żadnego serwera aplikacyjnego.

Dane strukturalne (profil, doświadczenie, projekty, technologie, ustawienia strony) opisane są w plikach JSON, a dłuższe opisy miejsc pracy i projektów — w języku znaczników Markdown, w osobnym pliku dla każdego języka.


Sekcje portfolio
  • O mnie
    • profil — awatar, odnośnik do GitHuba, tekst nagłówkowy oraz krótki opis
    • stos technologiczny — języki programowania, frameworki i biblioteki, narzędzia i środowisko
    • edukacja — uczelnia, kierunek, lata studiowania
    • certyfikaty — nazwa certyfikatu, wydawca, data wydania
    • języki obce
  • Doświadczenie — lista miejsc pracy: logo firmy z odnośnikiem, lata współpracy (z obsługą wariantu „teraz"), stos technologiczny oraz opis obowiązków.
  • Projekty — lista projektów: rok realizacji, stos technologiczny, opis, galeria multimediów oraz odnośnik do repozytorium; projekty o zamkniętym kodzie oznaczane są kłódką, a te w trakcie realizacji znacznikiem WIP.
  • Kontakt — adres e-mail.
Funkcje
  • Prerendering (SSG) — tryb outputMode: static generuje statyczny HTML dla każdej trasy i każdego języka. Wyszukiwarki i podglądy linków dostają gotową treść zamiast pustego <app-root>, a w przeglądarce strona hydratuje się do pełnej aplikacji SPA. Dane JSON i opisy Markdown pobrane w czasie budowania są osadzane w wynikowym HTML-u, więc przeglądarka nie pobiera ich po raz drugi.
  • Wielojęzyczność — język jest częścią adresu (/pl, /en), a każda wersja językowa ma własny, prerenderowany HTML. Teksty interfejsu pochodzą z plików tłumaczeń (@ngx-translate), a lista dostępnych języków z konfiguracji strony. Przy wejściu na adres główny język dobierany jest z wcześniejszego wyboru użytkownika, a w razie jego braku z preferencji przeglądarki. Przełącznik w pasku nawigacji zmienia język, pozostając na tej samej podstronie i bez przeładowania.
  • SEO — każda podstrona ma odnośnik canonical oraz komplet odnośników hreflang do swoich odpowiedników językowych (wraz z x-default), a atrybut lang w znaczniku <html> odpowiada wyświetlanej treści. Strona pobierania CV jest wyłączona z indeksowania.
  • Tryb jasny i ciemny — motyw oparty na zmiennych CSS, przełączany klasą na elemencie body, z płynnym przejściem kolorów. Wybór jest zapamiętywany i nakładany jeszcze przed pierwszym malowaniem strony, więc przy wejściu nie widać mignięcia drugiego motywu.
  • Galeria multimediów — obsługa obrazów i wideo. Typ pliku rozpoznawany jest po rozszerzeniu, miniatury przewijane są w poziomie, a podgląd otwiera się w oknie lightbox. GLightbox ładowany jest dopiero przy pierwszej galerii, jako jedna wspólna instancja dla całej strony.
  • Generator miniatur — skrypt tools/generate-thumbnails.py (Pillow, OpenCV) tworzy zoptymalizowane miniatury JPEG dla obrazów oraz z pierwszej klatki wideo, zgodnie z konwencją nazw thumb_<nazwa>.jpg.
  • Zwijane opisy — długie opisy projektów są domyślnie skracane, z gradientowym wygaszeniem i przyciskiem „Pokaż więcej". Przycisk pojawia się tylko wtedy, gdy treść przekracza zadaną wysokość, a wysokość przeliczana jest ponownie przy zmianie rozmiaru okna oraz po zmianie języka.
  • Znaczniki technologii — każdej technologii można przypisać kolor w pliku technologies.json; etykiety bez wpisu otrzymują kolor domyślny.
  • Animacje przy przewijaniu — sekcje i karty pojawiają się kaskadowo, wyzwalane przez IntersectionObserver; każdy element animuje się jednorazowo.
  • RWD — układ oparty na siatce Bootstrapa 5, z rozwijanym menu na urządzeniach mobilnych.
M2Chat
WIP
(2025)
Kod źródłowy
C#
ASP.NET
EF Core
Fluent Validation
.NET MAUI
Blazor Hybrid
Bootstrap
MudBlazor
PostgreSQL

Aplikacja desktopowa do zaawansowanego monitorowania czatu w grze Metin2. Czat odczytywany jest wyłącznie z obrazu — okno gry przechwytywane jest co 100 ms, a tekst rozpoznawany autorskim algorytmem OCR działającym na pikselach. Aplikacja nie ingeruje w proces gry: nie modyfikuje plików klienta, nie czyta pamięci i nie wstrzykuje kodu.

Klient to Blazor Hybrid (.NET MAUI, .NET 10) z interfejsem opartym o MudBlazor; przechwytywanie ekranu jest zaimplementowane dla Windows (P/Invoke user32/gdi32: GetDC + BitBlt na obszarze klienta wskazanego okna). Backendem jest ASP.NET Core Web API z bazą PostgreSQL.


Funkcje programu
  • Wybór okna gry (dowolne okno wskazywane przez użytkownika) i ciągły odczyt czatu
  • Podział na wiadomości graczy i komunikaty systemowe (rozpoznawane po kolorze tekstu)
  • Grupowanie duplikatów w listę unikalnych wiadomości ze znacznikami pierwszego i ostatniego wystąpienia
  • Wyłuskiwanie linków do przedmiotów ([nazwa]) jako osobnych obiektów w wiadomości
  • Tracker bossów: pozostała liczba sztuk na każdym kanale + odliczanie do respawnu
  • Feed pokonanych bossów (czas, kanał, gracz, boss)
  • Rankingi zabójstw bossów wg gracza, z konfigurowalnym oknem czasowym
  • Statystyki aktywności czatu (unikalne wiadomości na minutę)
  • Konfiguracje pod różne serwery gry, z możliwością udostępniania innym użytkownikom
Rozpoznawanie znaków

Zamiast gotowego silnika OCR zastosowano dopasowanie wzorców bitowych, dobrze sprawdzające się przy stałej, rastrowej czcionce gry:

  • Budowa charsetu — bitmapa czcionki (charset.bmp + lista znaków w charset.txt) jest skanowana kolumnami; każdy glif kodowany jest jako maska UInt128 i zapisywany w słowniku w obie strony (maska <-> znak).
  • Odczyt linii — dla każdej z 20 linii czatu (od dołu w górę) skanowane są kolejne kolumny pikseli. Piksel w jednym z kolorów zdefiniowanych w konfiguracji ustawia bit w buforze UInt128 (|= z przesunięciem bitowym); pusta kolumna kończy znak i wyzwala odpytanie słownika.
  • Sklejone glify — gdy pełna maska nie ma dopasowania, bufor jest kolejno przycinany maską bitową od najdłuższego wariantu, dopasowany fragment zdejmowany przesunięciem bitowym, a reszta trafia do następnej iteracji.
  • Spacje — wykrywane na podstawie liczby następujących po sobie pustych kolumn.
  • Nieznane znaki — zapisywane do logu wraz z wycinkiem bitmapy, co pozwala uzupełnić charset.
  • Kolor znaku niesie informację semantyczną: rozróżnia komunikat systemowy od wiadomości gracza oraz typ przedmiotu w linku (ekwipunek / pozostałe).
Konfiguracje serwerów gry

Serwery prywatne różnią się czcionką, kolorami czatu, formatem komunikatów, liczbą kanałów, godziną startu serwera oraz listą bossów (nazwa, liczba sztuk, czas respawnu).

  • Wzorce komunikatów zapisywane są ze znacznikami {Channel}, {Player}, {Boss} i kompilowane do wyrażeń regularnych z grupami nazwanymi, z których wyłuskiwane są dane o zabójstwie.
  • Czasy respawnu wyliczane są z godziny startu serwera i interwału bossa; licznik na trackerze resetuje się automatycznie 3 minuty przed respawnem (z zabezpieczeniem przed podwójnym resetem).
Usługa Web API

Minimal API na ASP.NET Core (.NET 10), EF Core + PostgreSQL, walidacja przez FluentValidation.

  • Uwierzytelnianie użytkowników za pomocą ASP.NET Core Identity (uwierzytelnianie ciasteczkowe).
  • Wersjonowanie konfiguracji: każdy zapis tworzy nową wersję (numer, JSON, hash) powiązaną z wpisem nadrzędnym.
  • Użytkownik może udostępnić konfigurację publicznie; inni mogą ją zaimportować (zliczana jest liczba importów).
  • Import zapamiętuje numer wersji — po aktualizacji przez autora importujący dostają informację o dostępnej nowej wersji i synchronizują ją w dowolnym momencie.
  • Autor może zmienić widoczność konfiguracji lub ją usunąć (soft-delete); użytkownicy, którzy ją wcześniej zaimportowali, zachowują do niej dostęp.
Planowane funkcje
  • Filtrowanie wiadomości
  • Powiadomienia o zdarzeniach na czacie
  • Wyciszanie graczy
  • Odczytywanie bonusów przedmiotów z czatu
  • Rozbudowa panelu statystyk
  • Streaming czatu
Calyx Engine
(2024)
C++
OpenGL
GLM
ImGui
EnTT
Box2D
GameNetworkingSockets
Premake5

Silnik do tworzenia gier 2D napisany w C++ z edytorem graficznym: renderer OpenGL, Entity Component System (EnTT), symulacja fizyki (Box2D), natywne skryptowanie oraz autorski moduł sieciowy oparty o GameNetworkingSockets.

Rdzeń architektury bazuje na otwartoźródłowej wersji silnika Hazel, jednak wprowadzono w nim szereg modyfikacji i autorskich rozwiązań — największym z nich jest w pełni własny moduł sieciowy. Jest to projekt hobbystyczny na wczesnym etapie rozwoju, nieprzeznaczony do produkcji komercyjnych gier; rozwój jest obecnie wstrzymany.

W ramach pracy magisterskiej silnik został rozszerzony o moduł sieciowy, który następnie poddano analizie pod kątem wpływu opóźnień sieciowych na jakość symulacji rozgrywki. W szczególności zbadano, jak różne wartości parametrów algorytmu predykcji po stronie klienta oddziałują na częstotliwość występowania oraz skalę desynchronizacji.

Projekt konfigurowany jest przez Premake5 (Windows, Visual Studio) i budowany w trzech konfiguracjach: Debug, Release (z edytorem) oraz Distribution — wersja dystrybucyjna bez edytora, stanowiąca runtime gry.

Silnik powstawał wcześniej pod nazwą Proton2D — cała historia gita znajduje się w starym repozytorium https://github.com/Damian-Zuk/proton2d.


Edytor (ImGui)
  • Viewport, hierarchia sceny, inspektor obiektów, przeglądarka treści (tekstury, sceny, prefabrykaty), panel ustawień oraz panel statystyk.
  • Sterowanie symulacją: start / pauza / stop, ze zrzutem stanu sceny i przywróceniem go po zatrzymaniu.
  • Wiele instancji gry w jednym procesie — każda z własnym viewportem, menedżerem scen i menedżerem sieci, co pozwala uruchomić serwer i kilku klientów obok siebie i testować rozgrywkę multiplayer bez opuszczania edytora (instancja klienta może startować automatycznie po uruchomieniu serwera).
Renderer 2D (OpenGL / Glad)
  • Batch renderer — domyślnie do 10 000 quadów w jednej partii, liczba slotów tekstur odczytywana ze sterownika (do 32); osobne potoki dla quadów, okręgów, linii i tekstu.
  • Sprite'y i atlasy tekstur (TextureAtlas, Sprite), skalowanie techniką 9-slice, animacje oparte na atlasie.
  • Rendering tekstu z atlasów MSDF (msdf-atlas-gen), kamera ortograficzna z zoomem, framebuffery, licznik wywołań rysowania.
Scena, ECS i zasoby
  • Scene opakowuje rejestr EnTT; Entity to para identyfikatora i wskaźnika na scenę, z UUID nadawanym każdemu obiektowi.
  • Hierarchia rodzic-dziecko realizowana komponentem relacji (wskaźniki na pierwsze dziecko oraz rodzeństwo), z rozdzieleniem pozycji lokalnej i światowej.
  • Serializacja i deserializacja scen oraz prefabrykatów do JSON; prefabrykat jest szablonem obiektu współdzielonym przez sceny i wykorzystywanym przy spawnie po sieci.
  • Fizyka aktualizowana w stałym kroku czasowym (akumulator), niezależnie od liczby klatek na sekundę.
Skryptowanie natywne w C++
  • Hierarchia klas AppScript > GameScript > EntityScript (logika aplikacji, trybu gry i pojedynczego obiektu), fabryka skryptów tworząca instancje po nazwie klasy oraz makro rejestrujące.
  • Pola skryptów rejestrowane makrem (REGISTER_FIELD) trafiają do inspektora i są serializowane wraz ze sceną; obsługiwane typy to wartości skalarne, wektory, tekst i dowolne dane trywialnie kopiowalne.
  • Pole oznaczone jako replikowane (REPLICATED_FIELD) jest automatycznie synchronizowane po sieci, opcjonalnie z funkcją wywoływaną po odebraniu nowej wartości.
  • Brak hot-reloadingu; docelowo planowany był silnik skryptowy C#.
Fizyka (Box2D)

Ciała sztywne, kolidery prostokątne i okrągłe wraz z parametrami materiału i filtrami kolizji, sensory, nasłuchiwanie kontaktów z wywołaniem zwrotnym w skrypcie oraz złączenia obrotowe (przypięcie obiektu do rodzica).

Moduł sieciowy

Architektura klient-serwer z w pełni autorytatywnym serwerem, w trybie listen server, dedykowanym lub klienta. Cała komunikacja odbywa się na własnym binarnym strumieniu (zapis nagłówka wstecz po wyliczeniu rozmiaru ładunku), a każda odczytana wartość jest walidowana względem pozostałego rozmiaru bufora — niespójny pakiet kończy się rozłączeniem klienta zamiast odczytu poza zakres.

  • Protokół — handshake przenosi wersję protokołu silnika i gry; niezgodność którejkolwiek z nich zrywa połączenie z konkretnym kodem błędu. Typy wiadomości obejmują spawn i despawn obiektów, replikację, potwierdzenia numerów sekwencyjnych oraz wiadomości niestandardowe wysyłane ze skryptów (brak RPC).
  • Replikacja różnicowa — serwer utrzymuje ostatnio wysłany stan osobno dla każdego klienta i wysyła wyłącznie to, co się zmieniło: składowe transformacji zaznaczane są flagami bitowymi (PositionX, PositionY, ScaleX, ScaleY, Rotation), a zmiany pól skryptów wykrywane sumą kontrolną CRC32 liczoną per klient. Obiekt, dla którego nic się nie zmieniło, jest wycofywany ze strumienia.
  • Cull distance — obiekty oddalone od gracza poza zadany promień nie są replikowane, a po stronie klienta ich ciała fizyczne są usypiane.
  • Dołączanie w trakcie gry — serwer prowadzi rejestr wszystkich spawnów i despawnów, dzięki czemu nowy klient otrzymuje pełny stan sceny oraz wymuszoną pełną replikację. Obiekt przesyłany jest jako identyfikator prefabrykatu, a gdy nim nie jest — jako pełny opis JSON.
  • Tickrate replikacji (domyślnie 64 Hz) niezależny od liczby klatek, opcjonalnie zsynchronizowany z tickiem fizyki.
Synchronizacja transformacji

Metoda synchronizacji ustawiana jest per obiekt w inspektorze, wraz z progami rekoncyliacji, teleportacji i czasem jej trwania:

  • Interpolacja — ruch odtwarzany pomiędzy dwoma ostatnimi stanami autorytatywnymi.
  • Ekstrapolacja — pozycja przewidywana na podstawie prędkości szacowanej jako mieszanka prędkości lokalnego ciała Box2D i prędkości wynikającej z dwóch ostatnich stanów z serwera, wysunięta o zmierzone opóźnienie.
  • Predykcja po stronie klienta (opis metody) — zamiast powtarzania kolejki wejść klient buforuje różnice transformacji z każdego ticku wraz z numerem sekwencyjnym i wysyła ten numer do serwera. Serwer odsyła ostatni przetworzony numer w wiadomości replikacyjnej, klient odrzuca potwierdzone różnice i nakłada pozostałe na stan autorytatywny, otrzymując pozycję przewidywaną.
  • Rekoncyliacja — korekta błędu jest rozłożona w czasie (interpolacja do stanu przewidywanego z kompensacją różnic narastających w trakcie korekty), zamiast skokowego przeskoku; dopiero przekroczenie progu teleportacji powoduje natychmiastowe przeniesienie obiektu.
  • Dynamiczna ekstrapolacja — autorskie rozwiązanie: obiekt synchronizowany interpolacją przełącza się na ekstrapolację w momencie kontaktu fizycznego z obiektem predykowanym (sterowanym lokalnie) i wraca do interpolacji, gdy rozbieżność wobec stanu serwera spadnie poniżej progu. Ogranicza to widoczne rozjeżdżanie się obiektów przy zderzeniach z postacią gracza.
Narzędzia diagnostyczne i badawcze
  • Symulacja opóźnienia sieciowego ustawiana z panelu edytora (sztuczne opóźnienie pakietów przychodzących i wychodzących).
  • Zapis statystyk sieciowych do pliku — ping, przepustowość w obu kierunkach oraz średnia liczba replikowanych obiektów na wiadomość, próbkowane w stałych odstępach i opisane nagłówkiem ze sceną i tickrate'em; dane te posłużyły do pomiarów w pracy magisterskiej.
  • Podgląd statystyk w edytorze: liczba obiektów (w tym skryptowanych i sieciowych), czas klatki, ping i przepustowość dla każdego klienta.
  • Makra asercji, instrumentation profiler (zapis przebiegu do formatu śledzenia) oraz logger (spdlog).
CVE Report Generator
(2024)
Python
HTML
JavaScript
CSS
NVDLib
JSON
Nmap XML

Narzędzie CLI w Pythonie generujące raport podatności (CVE) dla urządzeń IoT. Na wejściu przyjmuje ręcznie zdefiniowaną listę urządzeń w formacie JSON (model, wersja firmware'u, adres MAC) i/lub skany sieci wykonane narzędziem Nmap (XML). Dla każdego zidentyfikowanego elementu oprogramowania — firmware'u, usług na otwartych portach oraz systemu operacyjnego — skrypt odpytuje bazę National Institute of Standards and Technology National Vulnerability Database (NIST NVD) i dołącza znalezione podatności do raportu wyjściowego.

Projekt zrealizowany w ramach przedmiotu „Bezpieczeństwo systemów IoT" na Politechnice Rzeszowskiej.


Przebieg działania
  1. Wczytanie listy urządzeń (devices.json) — dane wpisane ręcznie stanowią bazę raportu i mają pierwszeństwo przed danymi ze skanów.
  2. Parsowanie skanów Nmap (XML, nmap -A -oX) — hosty dopasowywane są do urządzeń z listy po adresie MAC; nieznane hosty trafiają do raportu jako nowe wpisy. Wyłuskiwane są: adres IPv4, producent (z MAC), wyłącznie porty w stanie open wraz z usługami oraz wynik detekcji systemu operacyjnego.
  3. Ustalenie identyfikatorów CPE — dla elementów bez CPE wykonywane jest wyszukiwanie po słowach kluczowych (model + wersja, produkt + wersja, nazwa OS + rodzina + wersja). Identyfikatory w starszym formacie CPE 2.2 są normalizowane do CPE 2.3.
  4. Pobranie podatności — zapytania po cpeName; gdy dla urządzenia nie udało się ustalić CPE, wykonywane jest wyszukiwanie po słowach kluczowych, a CPE jest uzupełniane na podstawie pierwszej dopasowanej podatności.
  5. Zapis raportu do pliku JSON o strukturze analogicznej do wejściowej, rozszerzonej o sekcję vulnerabilities.
Zawartość raportu

Raport to tablica urządzeń; każdy wpis zawiera:

  • Identyfikacja urządzenia — model, opis, adres MAC, adres IPv4, producent, wersja firmware'u oraz przypisany CPE. Dowolne własne pola z pliku wejściowego są przenoszone do raportu i wyświetlane przez przeglądarkę raportów.
  • Otwarte porty i usługi — numer portu, nazwa usługi, produkt, wersja i CPE.
  • System operacyjny — nazwa, informacja o dopasowaniu przez Nmap wraz z procentową trafnością oraz lista klas OS (producent, rodzina, wersja, trafność, CPE).
  • Podatności w trzech kategoriach: firmware, service (klucz: produkt wersja) i os (klucz: rodzina wersja). Każda kategoria zawiera łączną liczbę znalezionych CVE oraz ich listę, a pojedynczy wpis: identyfikator CVE, datę publikacji, opis, użytą metrykę CVSS, wynik punktowy i poziom istotności (LOW / MEDIUM / HIGH / CRITICAL).
Konfiguracja (config.ini)
  • Ścieżki: pliku wyjściowego, listy urządzeń oraz katalogu lub pojedynczego pliku ze skanami Nmap.
  • Klucz API do NIST NVD — skraca odstęp między zapytaniami z 6 s do 0,6 s.
  • Lista systemów operacyjnych pomijanych przy odpytywaniu bazy (np. linux, android), dla których liczba trafień jest zbyt duża, by wynik był użyteczny; w raporcie zapisywana jest wtedy adnotacja o pominięciu.
Przeglądarka raportów

Statyczna strona report-viewer.html (bez backendu i zależności) — plik JSON wczytywany jest metodą przeciągnij i upuść. Urządzenia prezentowane są jako rozwijane panele, poziomy istotności wyróżnione kolorem, a identyfikatory CVE i CPE linkują bezpośrednio do odpowiednich stron NVD. Ten sam widok obsługuje plik wejściowy z listą urządzeń, ponieważ ma on identyczną strukturę.

Ograniczenia
  • Dopasowanie CVE opiera się na konkretnym CPE — zakresy wersji podatnego oprogramowania (versionEndIncluding) nie są analizowane, więc podatność opisana zakresem może nie zostać wykryta.
  • Trafność wyszukiwania po słowach kluczowych zależy od nazewnictwa producenta w bazie NVD.
Ansible Project
(2024)
Ansible
Ansible Semaphore
Ubuntu
AWS
PHP
MySQL
Apache

Projekt studencki z wykorzystaniem narzędzia Ansible do automatyzacji zarządzania konfiguracją maszyn wirtualnych, instalacji pakietów systemowych oraz wdrożenia prostej aplikacji w technologii LAMP.

Całość zrealizowana została w chmurze AWS — na osobnych instancjach uruchomiono węzeł zarządzający oraz zarządzane serwery webowe. Playbooki wykonywane są przez Ansible Semaphore, czyli graficzny interfejs dla Ansible, który udostępnia uruchamianie zadań z poziomu przeglądarki, harmonogramy, magazyn kluczy dostępowych oraz historię i logi wykonania.

Zasadniczą częścią projektu było udokumentowanie konfiguracji całego rozwiązania — od przygotowania instancji i dostępu SSH, przez instalację i konfigurację Semaphore, po opis działania poszczególnych ról. W repozytorium znajdują się wyłącznie pliki Ansible: playbooki, role oraz inwentarz.


Role Ansible
  • create_admin_user — Zarządzanie kontami administratorów systemu.
    • Tworzy użytkowników na podstawie listy admin (z określonym UID i opisem).
    • Konfiguruje dostęp SSH poprzez dodanie kluczy publicznych do authorized_keys.
    • Nadaje uprawnienia sudo poprzez modyfikację pliku /etc/sudoers.
  • update_system — Rola odpowiedzialna za aktualizację systemu operacyjnego.
    • Odświeża cache pakietów APT.
    • Aktualizuje wszystkie zainstalowane pakiety do najnowszych dostępnych wersji.
  • setup_apache_and_php — Instalacja i konfiguracja środowiska webowego.
    • Instaluje serwer HTTP Apache.
    • Instaluje PHP wraz z wymaganymi modułami.
    • Włącza moduł PHP w Apache.
    • Modyfikuje konfigurację PHP: zwiększa memory_limit na 256 MB.
    • Restartuje usługę Apache w celu zastosowania zmian.
  • setup_mysql_db — Instalacja i konfiguracja serwera bazy danych MySQL.
    • Instaluje wymagane zależności (pip, PyMySQL).
    • Instaluje i uruchamia serwer MySQL.
    • Tworzy użytkownika MySQL z pełnymi uprawnieniami.
    • Tworzy bazę danych aplikacji.
    • Tworzy tabelę posts, jeśli nie istnieje.
    • Wstawia przykładowe dane do tabeli posts na podstawie szablonu SQL.
  • deploy_website — Wdrożenie aplikacji webowej oraz jej konfiguracji.
    • Pobiera najnowszą wersję aplikacji z repozytorium Git.
    • Synchronizuje kod do katalogu /var/www.
    • Aktualizuje pliki tylko w przypadku wykrycia zmian.
    • Tworzy katalog konfiguracyjny aplikacji.
    • Ładuje zmienne bazy danych.
    • Generuje plik konfiguracyjny config.php na podstawie szablonu Jinja2.
    • Wstrzykuje dane dostępowe do bazy oraz parametry strony.
Poll Wizard
(2023)
Python
FastAPI
SQLAlchemy
Pydantic
PostgreSQL
Redis
TypeScript
React.JS
Bootstrap
Docker

Aplikacja internetowa do tworzenia prostych ankiet jednopytaniowych i głosowania, inspirowana platformą StrawPoll.com.

Backend to REST API na FastAPI (SQLAlchemy ORM, PostgreSQL, walidacja przez Pydantic) z Redisem pełniącym rolę magazynu stanu sesji i licznika limitów zapytań. Frontend to aplikacja jednostronicowa w React + TypeScript (Vite, Bootstrap). Całość uruchamiana jest przez Docker Compose, a dokumentacja API generowana automatycznie (Swagger).


Funkcje aplikacji
  • Konta użytkowników — rejestracja z reCAPTCHA, logowanie oraz uwierzytelnianie tokenami JWT.
  • Tworzenie ankiet — pytanie oraz od 2 do 16 unikalnych odpowiedzi (walidacja po stronie API).
  • Głosowanie — jeden głos na ankietę dla zalogowanego użytkownika, z natychmiastowym podglądem wyników (liczba głosów i udział procentowy na paskach).
  • Profil użytkownika — lista ankiet utworzonych przez daną osobę; autor może usunąć własną ankietę.
  • Rate limiting — limity zapytań liczone w Redisie, osobno dla całych grup punktów końcowych i dla operacji wrażliwych (np. 3 rejestracje na minutę, 5 nowych ankiet na minutę).
Autorskie rozszerzenie JWT z magazynem w Redisie

Zamiast prostego wystawiania tokenów napisałem osobny moduł zarządzania sesjami (fastapi_jwt_redis), integrujący się z systemem zależności FastAPI:

  • Para tokenów — krótko żyjący token dostępowy i token odświeżający, każdy z własnym identyfikatorem jti, czasem ważności i czasem rozpoczęcia ważności.
  • Rotacja tokenów odświeżających — każde odświeżenie sesji wydaje nową parę tokenów; stary token odświeżający zostaje oznaczony w Redisie jako zużyty.
  • Wykrywanie ponownego użycia — próba użycia tokenu odświeżającego po raz drugi (typowy objaw kradzieży tokenu) jest traktowana jako naruszenie: żądanie zostaje odrzucone, a cała sesja unieważniona.
  • Kaskadowe unieważnianie — Redis przechowuje powiązania między tokenami powstałymi w kolejnych rotacjach, dzięki czemu wylogowanie lub wykryte nadużycie rekurencyjnie wpisuje na czarną listę cały łańcuch tokenów wywodzących się z danej sesji, a nie tylko token okazany w żądaniu.
  • Weryfikacja w punkcie wejścia — własna klasa zabezpieczająca sprawdza schemat nagłówka, podpis, czas ważności, zgodność typu tokenu (token odświeżający nie zostanie przyjęty tam, gdzie wymagany jest token dostępowy) oraz obecność identyfikatora na czarnej liście.
  • Wpisy w Redisie mają czas życia równy ważności tokenu odświeżającego, więc magazyn czyści się sam.
Bezpieczeństwo i walidacja
  • Hasła haszowane algorytmem Argon2, z wymogiem złożoności (długość, wielkie i małe litery, cyfra, znak specjalny) sprawdzanym przy rejestracji.
  • Walidacja danych wejściowych schematami Pydantic: ograniczenia długości pytania i odpowiedzi, wymóg unikalności odpowiedzi, kontrola formatu nazwy użytkownika oraz zajętości adresu e-mail i loginu.
  • Autoryzacja operacji na zasobach — usunąć ankietę może wyłącznie jej autor; ponowny głos w tej samej ankiecie jest odrzucany po stronie API.
  • Konfiguracja wrażliwych parametrów (sekret JWT, czasy ważności, dane baz) wyłącznie ze zmiennych środowiskowych; endpointy deweloperskie rejestrowane tylko w trybie debug.
Frontend

Aplikacja React z routingiem po stronie klienta i cyklicznym odświeżaniem sesji w tle (token dostępowy wymieniany zanim wygaśnie). Widok pojedynczej ankiety pod własnym adresem umożliwia jej udostępnianie, a lista głosów oddanych przez zalogowanego użytkownika pobierana jest jednym zapytaniem zbiorczym dla wszystkich widocznych ankiet.

Brakujące kluczowe funkcje aplikacji
  • paginacja,
  • GUID dla linków do ankiet zamiast ID,
  • niepubliczne ankiety (dostęp tylko z linkiem),
  • możliwość głosowania bez konta,
  • limit głosów na adres IP.
Auto Parts
(2021)
Python
Django
PostgreSQL
Bootstrap
JavaScript
Leaflet
Heroku
PWA
Agile Scrum

Aplikacja sklepu internetowego do sprzedaży części samochodowych. Stworzona została w grupie 5-osobowej we współpracy z grupą wydziału zarządzania w metodyce Scrum. Aplikacja zrealizowana została w ramach zajęć projektowych na studiach z przedmiotu „Inżynieria Oprogramowania i Baz Danych”.

Zbudowana w Django (Python) z bazą PostgreSQL, szablonami po stronie serwera i interfejsem opartym o Bootstrap. Aplikacja jest wielojęzycznie skonfigurowana pod rynek polski, działa jako PWA i została wdrożona na Heroku (gunicorn, whitenoise).

Mój zakres prac obejmował katalog produktów i warstwę prezentacji: filtrowanie oraz sortowanie listy produktów, podział asortymentu na sekcje i kategorie wraz z paskiem nawigacji, paginację, obsługę cen promocyjnych z widokiem wyprzedaży i sekcją ofert specjalnych, a także wydzielenie zapytań widoku głównego do osobnej warstwy serwisowej. Po stronie interfejsu przygotowałem m.in. responsywny pasek nawigacji, pływający koszyk oraz szablony strony produktu, listy produktów, logowania i rejestracji oraz podsumowania zamówienia.


Katalog produktów
  • Kategorie hierarchiczne — kategoria może mieć kategorię nadrzędną; przeglądanie działu pokazuje produkty ze wszystkich jego podkategorii, a pasek nawigacji przełącza się na podkategorie wybranego działu.
  • Filtrowanie i sortowanie (django-filter) po nazwie, cenie i promocji, z paginacją po 12 pozycji. Sortowanie po cenie uwzględnia cenę efektywną — wyrażenie SQL wybiera cenę promocyjną, gdy istnieje, a w przeciwnym razie cenę podstawową.
  • Wyszukiwarka przeszukująca nazwy i opisy produktów.
  • Promocje — osobny widok wyprzedaży oraz sekcja ofert specjalnych na stronie głównej; przy produkcie prezentowana jest cena przed i po obniżce oraz kwota oszczędności.
  • Parametry produktów przechowywane w polu JSON, co pozwala opisywać różne kategorie części różnymi zestawami atrybutów bez zmiany schematu bazy.
Porównywarka produktów

Zbudowana na otwartoźródłowym module dla Django (lista porównywanych pozycji trzymana w sesji, dodawanie i usuwanie przez AJAX), rozszerzonym o zestawienie parametrów z pola JSON: atrybuty wszystkich porównywanych produktów są scalane w jeden zbiór i układane w tabelę wiersz po wierszu, a brakujące wartości oznaczane myślnikiem. Porównywanie odbywa się w obrębie jednej kategorii.

Placówki i geolokalizacja
  • Punkty sprzedaży z adresem, telefonem, zdjęciem i współrzędnymi geograficznymi, wraz z wyszukiwarką po mieście, adresie, kodzie pocztowym i numerze telefonu.
  • Wyszukiwanie najbliższej placówki — przeglądarka pobiera pozycję użytkownika (Geolocation API), a backend wylicza najbliższy punkt zapytaniem SQL opartym na wzorze haversine, bez potrzeby użycia GeoDjango. Wynik zwracany jest jako JSON i nanoszony na mapę OpenStreetMap (Leaflet) obok znacznika pozycji użytkownika.
  • Stany magazynowe prowadzone są osobno dla każdej placówki (ilość produktu w danym punkcie).
Konta i zamówienia
  • Rejestracja z aktywacją e-mailem — konto pozostaje nieaktywne do czasu kliknięcia w link z jednorazowym tokenem; walidacja loginu i hasła realizowana własnymi walidatorami (minimalna długość, cyfra, wielka litera) z komunikatami po polsku.
  • Typy użytkowników — klient indywidualny oraz warsztat; typ konta zmienia dostępne metody płatności.
  • Koszyk — zamówienie w stanie „nieopłacone" pełni rolę koszyka, z dodawaniem, zmniejszaniem ilości i usuwaniem pozycji oraz przeliczaniem wartości z uwzględnieniem promocji.
  • Realizacja zamówienia — formularz adresu dostawy z wyborem kraju oraz metody płatności (BLIK, karta, Apple Pay, Google Pay, PayPal).
  • Faktura PDF — przy płatności fakturą (dostępnej dla warsztatów) generowany jest dokument z pozycjami zamówienia i stawką VAT, zapisywany przy zamówieniu i dostępny z historii zakupów.
  • Profil użytkownika z edycją danych kontaktowych oraz listą złożonych zamówień.
Pozostałe rozwiązania
  • Obrazy produktów, zdjęcia placówek i pliki faktur przechowywane są w bazie danych (osobne tabele na zawartość, nazwę i typ MIME) zamiast na dysku — wygodne przy wdrożeniu na platformie z efemerycznym systemem plików.
  • Panel administracyjny Django do zarządzania katalogiem, placówkami i zamówieniami, z polskimi nazwami modeli.
  • Polecenie zarządzające generujące dane testowe (kategorie, kilkaset produktów, placówki) na potrzeby developmentu.
Push The Box
(2020)
C++
SFML
CMake

Gra logiczna wzorowana na klasycznym Sokobanie — celem jest wepchnięcie wszystkich skrzynek na wyznaczone pola. Napisana w C++17 z użyciem biblioteki SFML, bez gotowego silnika: pętla gry, system stanów, encje, animacje oraz kontrolki interfejsu zostały zaimplementowane od zera. Interfejs gry jest w języku polskim.

Projekt budowany jest przez CMake i przeznaczony na platformę Win32 (x86), zgodnie z dołączoną wersją SFML.


Szkielet aplikacji
  • Pętla gry z pomiarem czasu klatki (delta time) i limitem FPS; przy starcie wczytywana jest konfiguracja okna oraz lista zasobów.
  • Stos stanów — menu główne, opcje, wybór poziomu, rozgrywka i edytor są osobnymi stanami; aktualizowany i rysowany jest wyłącznie stan ze szczytu stosu, a jego zamknięcie zwalnia wszystkie należące do niego encje.
  • Warstwy encji — przynależność do maksymalnie 32 warstw zapisana jako maska bitowa, co pozwala pokazywać i ukrywać całe grupy elementów w obrębie jednego stanu (np. w menu edytora: widok główny / nowy poziom / wczytywanie).
  • Encje z hierarchią rodzic-dziecko, przypinaniem pozycji do innej encji, animacją ze spritesheetu i płynnym ruchem do zadanego punktu.
  • Układ niezależny od rozdzielczości — pozycje i rozmiary opisywane są we współrzędnych znormalizowanych względem modelu 1920×1080 i przeliczane przez skalę okna.
  • Menedżer zasobów ładujący tekstury i czcionki na podstawie zewnętrznego pliku listy zasobów (nazwa pliku, identyfikator, wygładzanie).
  • Własne kontrolki UI: przycisk, checkbox, lista rozwijana, pole tekstowe (limit znaków, tryb tylko cyfr, migający kursor) oraz etykieta tekstowa.
Rozgrywka
  • Ruch w czterech kierunkach z animacją postaci; pchnięcie skrzynki dochodzi do skutku tylko wtedy, gdy pole za nią jest wolne — kolizje sprawdzane są na siatce identyfikatorów kafli, nie na sprite'ach.
  • Cofanie ruchów — rejestr do 10 ostatnich ruchów (kierunek, animacja i ewentualnie pchnięta skrzynka) działający jak bufor FIFO; cofnięcie odtwarza ruch gracza razem ze skrzynką i aktualizuje siatkę kolizji.
  • Licznik ruchów i czasu, restart poziomu oraz sterowanie z klawiatury lub przyciskami ekranowymi.
  • Kamera podąża za graczem z marginesem od krawędzi kadru i blokuje daną oś, gdy plansza mieści się w całości na ekranie.
  • Poziom kończy się w momencie zajęcia wszystkich pól docelowych; wynik trafia do zapisu tylko wtedy, gdy jest lepszy od poprzedniego (mniej ruchów lub krótszy czas).
  • 16 wbudowanych poziomów oraz osobne menu poziomów własnych.
Binarny format poziomu

Nagłówek (18 bajtów): wymiary planszy, pozycja startowa gracza i liczba skrzynek. Dalej tablica pozycji pól docelowych (po 8 bajtów) oraz siatka kafli zapisana po jednym bajcie na pole (brak kafla / podłoga / ściana / skrzynka).

Wariant tekstury podłogi i ściany losowany jest z ziarna wyliczonego z wymiarów poziomu, dzięki czemu plansza wygląda różnorodnie, a jednocześnie identycznie przy każdym wczytaniu.

Edytor poziomów
  • Tworzenie nowego poziomu (nazwa, wymiary od 5 do 99 na oś) albo wczytanie istniejącego do dalszej edycji.
  • Narzędzia do stawiania ścian, podłogi, pól docelowych i punktu startowego gracza, gumka oraz regulacja liczby skrzynek; planszę większą od kadru przewija się strzałkami.
  • Walidacja przy zapisie — sprawdzane jest ustawienie punktu startowego, obecność skrzynek i pól docelowych oraz zgodność ich liczby; komunikat wypisuje wszystkie wykryte problemy naraz.
  • Zapisane poziomy trafiają do katalogu poziomów własnych i są od razu dostępne w menu gry.
Zapis postępów i ustawień
  • Postępy przechowywane są w binarnym pliku z rekordem na każdy poziom (ukończenie, najlepszy czas, najmniejsza liczba ruchów), poprzedzonym sumą kontrolną SHA-256 liczoną z danych i stałego ciągu. Niezgodność sumy oznacza ręczną modyfikację lub uszkodzenie pliku i skutkuje zresetowaniem postępu.
  • Ustawienia obrazu (pięć predefiniowanych rozdzielczości, tryb pełnoekranowy) zapisywane są do pliku konfiguracyjnego; zastosowanie zmian odtwarza okno i przelicza układ interfejsu bez restartu gry.
Console Chat
(2019)
C++
Winsock
Diffie-Hellman Key Exchange
SHA256

Konsolowy komunikator internetowy klient-serwer na platformę Windows, napisany w C++ na gniazdach Winsock (TCP), bez zewnętrznych bibliotek. Serwer i klient to dwie osobne aplikacje uruchamiane z linii poleceń; cała logika — konta, rangi, uprawnienia, bany — znajduje się po stronie serwera, a klient odpowiada wyłącznie za wyświetlanie i przekazywanie tego, co wpisze użytkownik.

Wczesny projekt hobbystyczny, powstały z chęci poznania programowania sieciowego i wielowątkowości.


Architektura i protokół
  • Wątek na klienta — wątek główny przyjmuje połączenia, każde uwierzytelnienie i obsługa sesji dostają własny wątek; dostęp do plików serwera chroniony jest muteksem.
  • Własny protokół pakietowy — przed każdym pakietem wysyłany jest jego typ (wiadomość, komenda, prośba o dane), a struktura pakietu wybierana jest szablonem na podstawie typu przesyłanego obiektu.
  • Prośby o dane sterowane przez serwer — serwer może poprosić klienta o wprowadzenie tekstu, przekazując etykietę pola i informację o maskowaniu znaków. Na tym mechanizmie oparte są logowanie hasłem, zakładanie konta i potwierdzenia operacji, dzięki czemu klient nie musi znać żadnego z tych scenariuszy.
  • Tryb publiczny lub lokalny wybierany przy starcie, wraz z portem, liczbą slotów i opcjonalnym hasłem do serwera; przy pierwszym uruchomieniu tworzony jest katalog z plikami serwera i wiadomością powitalną (MOTD).
Szyfrowanie i uwierzytelnianie
  • Klient i serwer ustalają wspólny klucz metodą wymiany klucza Diffiego-Hellmana (potęgowanie modularne na ustalonej podstawie i module), a z uzgodnionego sekretu i nazwy użytkownika liczony jest skrót SHA-256, z którego pobierany jest właściwy klucz sesji.
  • Każdy pakiet w obu kierunkach jest szyfrowany strumieniem bajtów generowanym przez srand zainicjowany kolejnymi znakami klucza i mieszanym z danymi operacją XOR.
  • Hasła kont nie są przechowywane jawnie — w pliku serwera zapisywane są ich skróty SHA-256 (implementacja algorytmu dołączona do projektu).
  • Zastosowany szyfr jest rozwiązaniem autorskim o niskiej sile kryptograficznej — jego celem było poznanie mechaniki wymiany kluczy, a nie realna ochrona transmisji.
Konta, rangi i moderacja
  • Rangi: GUEST (niezarejestrowany), USER, VIP (kolory w wiadomościach), MOD i ADMIN, porównywane liczbowo — komenda wymaga rangi nie niższej niż jej próg.
  • Rejestracja polega na ustawieniu hasła do własnego nicku; przy kolejnych połączeniach serwer prosi o nie w zamaskowanym polu.
  • Komendy użytkownika: lista dostępnych poleceń, lista osób online, wiadomości prywatne wraz z szybką odpowiedzią do ostatniego rozmówcy, rozłączenie.
  • Komendy moderatora: szczegóły o użytkownikach online, wyrzucenie z serwera, bany na adres IP i przegląd listy banów.
  • Komendy administratora: nadawanie i odbieranie rang, usuwanie hasła z konta, przegląd kont i rang, a także zamknięcie lub restart serwera z możliwością anulowania operacji.
  • Bany czasowe i trwałe — czas trwania podaje się skrótowo (np. 7d, 12h, 30m), a termin wygaśnięcia zapisywany jest jako znacznik czasu; po jego minięciu wpis przestaje obowiązywać, a pozostały czas prezentowany jest w czytelnej postaci.
  • Konta, rangi i bany trzymane są w plikach tekstowych serwera, wczytywanych przy starcie i aktualizowanych na bieżąco.
MTConsole — własna biblioteka konsoli wielowątkowej

Zwykła konsola nie nadaje się do czatu: tekst przychodzący z sieci rozbija linię, którą użytkownik właśnie pisze. Na potrzeby projektu powstał osobny komponent rozwiązujący ten problem:

  • Obsługa wejścia w osobnym wątku, znak po znaku, z rysowaniem linii wprowadzania niezależnie od strumienia wiadomości — przed wypisaniem nowej linii pole wejściowe jest czyszczone i odtwarzane pod spodem, a wypisywanie chroni muteks.
  • Poruszanie kursorem w obrębie wpisywanego tekstu (strzałki lewo/prawo), wstawianie i kasowanie znaków w środku linii, z poprawną obsługą zawijania na krawędzi okna konsoli.
  • Historia wpisanych poleceń dostępna strzałkami góra/dół.
  • Maskowanie wejścia gwiazdkami przy wpisywaniu haseł oraz możliwość ukrycia i zablokowania pola wejściowego.
  • Kolorowanie tekstu znacznikami w formacie {numer} osadzanymi bezpośrednio w treści — ten sam ciąg może zostać wypisany z kolorami albo oczyszczony ze znaczników, co jest wykorzystywane m.in. przy wiadomościach użytkowników rangi VIP.
Flappy Bird Clone
(2018)
Python
Pygame

Klon gry Flappy Bird napisany w Pythonie z użyciem biblioteki Pygame. Pygame odpowiada wyłącznie za okno, rysowanie i zdarzenia wejścia — warstwa gry (obiekty, sceny, zasoby, pętla aktualizacji) została zbudowana od zera.


Szkielet gry
  • Pętla główna z pomiarem czasu klatki i limitem FPS; cały ruch liczony jest w jednostkach na sekundę i mnożony przez czas klatki, więc zachowanie gry nie zależy od wydajności komputera.
  • Hierarchia obiektów — bazowy obiekt gry (z metodą aktualizacji i flagą aktywności) rozszerzany przez obiekt rysowalny (pozycja, rozmiar, tekstura lub kolor wypełnienia). Ptak, rury, tło, teksty i przyciski to jego pochodne.
  • Menedżer zasobów — centralne rejestry obiektów aktualizowanych i rysowanych oraz słownik zasobów dostępnych po nazwie; lista rysowania sortowana jest według z_index, co ustala kolejność warstw.
  • Menedżer scen — scena to nazwana grupa obiektów przełączana jednym wywołaniem: tap (ekran startowy), playing, died i died_new_best. Zmiana stanu gry sprowadza się do włączenia jednej sceny i wyłączenia pozostałych, zamiast ręcznego ukrywania pojedynczych elementów.
  • Warstwa graficzna z globalnymi współczynnikami skali, przez które przechodzą wszystkie współrzędne i rozmiary, oraz z funkcją detekcji kolizji AABB.
  • Menedżer czcionek buforujący czcionki według nazwy i rozmiaru, a etykiety tekstowe generują teksturę ponownie tylko wtedy, gdy treść faktycznie się zmieniła.
  • Nakładka debugowania wyświetlająca w rogu ekranu liczbę klatek, czas klatki, zużycie pamięci procesu i liczbę aktywnych rur.
Generowanie i recykling rur
  • Ptak nie przemieszcza się w poziomie — gra prowadzi licznik pokonanego dystansu, a pozycje rur wyliczane są z jego wartości i indeksu rury. Dzięki temu świat jest nieskończony bez przechowywania jego historii.
  • Liczba obiektów rur jest stała i dobrana do szerokości ekranu; zamiast tworzyć i usuwać rury, gra przesuwa okno indeksów i ponownie wykorzystuje te same obiekty, nadając im nowe pozycje i wysokości.
  • Deterministyczne losowanie przerw — położenie prześwitu dla rury o danym numerze wyliczane jest z ziarna złożonego z czasu rozpoczęcia rozgrywki i indeksu rury. Ta sama rura zawsze otrzymuje ten sam prześwit, więc jej parametry można odtworzyć w dowolnym momencie, bez pamiętania wcześniejszych losowań.
  • Punkt przyznawany jest po minięciu przez ptaka połowy szerokości rury, a kolizja sprawdzana jest osobno dla części górnej i dolnej.
Sterowanie lotem

Prędkość opadania narasta do wartości granicznej, a przyspieszenie zależy od fazy lotu — wznoszenie hamowane jest mocniej niż swobodny spadek, co daje charakterystyczny łuk skoku. Po zderzeniu ptak przechodzi w osobny tryb upadku z większym przyspieszeniem i wyższą prędkością graniczną, zatrzymywany dopiero na poziomie ziemi, a ekran końcowy pojawia się z krótkim opóźnieniem.

Zapis najlepszego wyniku

Rekord przechowywany jest w pliku binarnym poprzedzonym skrótem SHA-256 liczonym z wyniku i stałego ciągu wpisanego w kod. Przy wczytywaniu skrót jest przeliczany ponownie i porównywany z zapisanym — niezgodność (czyli ręczna edycja pliku) lub jego uszkodzenie powoduje odrzucenie rekordu i rozpoczęcie liczenia od zera.

2D Platformer
(2018)
Kod źródłowy
C++
SDL2

Gra platformowa 2D napisana w C++ na bibliotece SDL2, która odpowiada wyłącznie za okno, rysowanie tekstur i odczyt klawiatury — pętla gry, obiekty, fizyka, kamera i wczytywanie map zostały napisane od zera.


Szkielet silnika
  • Pętla klatki z pomiarem czasu jej trwania; wszystkie prędkości wyrażone są w jednostkach na sekundę i mnożone przez ten czas, więc ruch nie zależy od wydajności komputera.
  • Hierarchia obiektów — bazowy obiekt gry (identyfikator, flaga aktywności, metoda aktualizacji) rozszerzany przez obiekt dynamiczny o pozycję, prędkość, rozmiar i masę. Gracz i bloki mapy są jego pochodnymi, dzięki czemu fizyka operuje na jednym wspólnym typie.
  • Rejestr obiektów prowadzony centralnie przez silnik, z pulą odzyskiwanych identyfikatorów — numer usuniętego obiektu wraca do puli i jest ponownie przydzielany kolejnemu.
  • Wyszukiwanie obiektów po typie zrealizowane szablonem zwracającym listę wszystkich obiektów dającej się rzutować klasy — z tego korzystają zarówno fizyka, jak i rysowanie.
  • Wirtualna rozdzielczość — świat i interfejs opisane są w stałym układzie odniesienia 1920×1080, a przy rysowaniu przeliczane przez współczynniki skali na faktyczny rozmiar okna.
  • Własny szablon wektora dwuwymiarowego z przeciążonymi operatorami arytmetycznymi oraz menedżer wejścia odpytujący stan klawiatury raz na klatkę.
Mapa i renderowanie
  • Poziom wczytywany jest z pliku tekstowego zawierającego rozmiar kafla, wymiary planszy w kaflach i listę identyfikatorów; niezerowy identyfikator tworzy blok kolizyjny. Testowa mapa ma 300×200 kafli po 32 piksele, co daje świat o rozmiarze 9600×6400 pikseli.
  • Kafle rysowane są z atlasu tekstur — identyfikator z pliku mapy wyznacza wycinek atlasu.
  • Odcinanie poza kadrem — przed rysowaniem sprawdzane jest, czy obiekt mieści się w obszarze widzianym przez kamerę; reszta planszy jest pomijana.
Fizyka i kolizje

Kolizje nie polegają na cofaniu obiektu po wykryciu przenikania. Dla każdej osi z osobna przeszukiwane są wszystkie obiekty leżące na drodze ruchu i wyznaczana jest najbliższa blokująca krawędź w kierunku prędkości, a nowa pozycja przycinana jest do niej. Dzięki temu obiekt nie przeskakuje przez przeszkodę nawet przy dużej prędkości, a granice mapy działają jak naturalne ograniczenie.

Grawitacja narasta wraz z masą obiektu do ustalonej prędkości granicznej, a kontakt z podłożem i sufitem zeruje prędkość pionową i aktualizuje informację o tym, czy obiekt znajduje się w powietrzu — z tego korzysta skok, wykonalny wyłącznie przy kontakcie z podłożem.

Sterowanie postacią

Ruch w poziomie rozpędza się od prędkości początkowej do maksymalnej, przy czym przyspieszenie w powietrzu jest niższe niż na ziemi, a maksymalna prędkość lotu nieco wyższa — daje to wyczuwalną różnicę między bieganiem a manewrowaniem w skoku. Zmiana kierunku w locie lekko koryguje prędkość pionową, a osobny klawisz przenosi gracza z powrotem na punkt startowy.

Kamera i tło z paralaksą
  • Kamera podąża za graczem z martwą strefą — przesuwa się dopiero, gdy postać wyjdzie poza wyznaczony obszar w kadrze — i jest przycinana do granic mapy, żeby nie pokazywać obszaru poza planszą.
  • Przy szybkim opadaniu punkt kadrowania płynnie przesuwa się w górę, odsłaniając więcej przestrzeni pod postacią, a po wyhamowaniu równie płynnie wraca do wartości domyślnej.
  • Warstwy tła mają zadaną odległość od kamery, a ich współczynnik przesunięcia wyliczany jest z niej i z kąta widzenia zgodnie z zależnością perspektywiczną, zamiast być dobierany ręcznie dla każdej warstwy. Opcjonalnie z odległości skalowany jest też rozmiar warstwy.
  • Warstwy powtarzane są w poziomie w nieskończoność, z korektą błędów zaokrągleń na styku sąsiednich kopii, a ta sama tekstura użyta wielokrotnie na różnych dystansach buduje wrażenie głębi dżungli.
Roulette Simulator
(2017)
HTML
CSS
JavaScript
Bootstrap
jQuery
SHA256

Symulator ruletki zainspirowany platformą CSGODouble, działający w całości w przeglądarce — bez backendu, na czystym JavaScripcie z jQuery i Bootstrapem. Wczesny projekt z 2017 roku, powstały podczas nauki podstaw JavaScriptu i tworzenia stron internetowych.

Koło ma 15 pól, a zakład stawia się na jeden z trzech kolorów:

  • czerwony (1–7) — wygrana 2X,
  • czarny (8–14) — wygrana 2X,
  • zielony (0) — wygrana 14X.

Generowanie wyniku (provably fair)

Algorytm odtwarza schemat „provably fair" stosowany na platformach tego typu — wynik jest ustalony jeszcze przed rozpoczęciem losowania i możliwy do zweryfikowania po fakcie. Wygrywająca liczba wyliczana jest z trzech wartości:

  • seed — sekretny ciąg znaków, udostępniany dnia następnego, aby można było zweryfikować prawidłowość losowań z dnia poprzedniego (generowany jako skrót losowego ciągu o długości 48–64 znaków),
  • public — wartość publiczna, którą jest data bieżącego dnia w formacie rok-miesiąc-dzień,
  • round — numer rundy w ramach danego dnia.

Wartości łączone są w jeden ciąg i przepuszczane przez funkcję skrótu SHA-256. Pierwsze osiem znaków wyniku odczytywane jest jako liczba szesnastkowa, a reszta z jej dzielenia przez 15 wyznacza wygrywające pole. Z kolejnych znaków tego samego skrótu wyliczane jest również przesunięcie w obrębie pola, dzięki czemu wskaźnik zatrzymuje się za każdym razem w innym miejscu tego samego pola — również w sposób weryfikowalny.

Animacja koła

Pola ułożone są na zapętlonej taśmie w kolejności naprzemiennej (czerwone i czarne po obu stronach zera), a losowanie polega na przesuwaniu tła o wyliczony dystans. Ruch wyhamowuje wykładniczo — w każdym kroku pokonywana jest część pozostałej odległości, więc koło zwalnia tym wolniej, im bliżej jest celu. Dystans startowy obejmuje kilka pełnych obrotów powiększonych o drogę do wylosowanego pola, a tempo wyhamowania jest regulowane suwakiem prędkości.

Rozgrywka
  • Odliczanie do startu rundy z paskiem postępu; na czas losowania obstawianie jest blokowane, a po ogłoszeniu wyniku numer rundy zwiększa się automatycznie.
  • Limity zakładów: minimalna stawka 10 żetonów oraz maksymalnie 3 zakłady na rundę.
  • Pole stawki z przyciskami skrótu (+10, +100, +1000, +10000, połowa, podwojenie, wszystko), a salda i pule na kolorach zmieniają się animowanym licznikiem, zielonym przy wygranej i czerwonym przy przegranej.
  • Darmowe żetony do odebrania, gdy saldo spadnie poniżej ustalonego progu.
  • Historia dziesięciu ostatnich wyników, efekty dźwiękowe kręcenia i zatrzymania oraz komunikaty o zdarzeniach.
Panel ustawień

Osobny panel pozwala samodzielnie sprawdzić działanie generatora: ręcznie ustawić lub wylosować ponownie seed, public i round, wyłączyć automatyczne zwiększanie numeru rundy, zmienić długość odliczania i prędkość animacji, znieść limit darmowych żetonów, a także uruchomić losowanie natychmiast, bez czekania na koniec odliczania. Wpisując te same trzy wartości, można odtworzyć dowolne wcześniejsze losowanie i potwierdzić, że wynik nie został zmieniony w trakcie.

Damian Żuk — Software Engineer © 2026