1 Commits

Author SHA1 Message Date
6e5317d471 Initial article 2025-10-24 20:50:44 +03:00
9 changed files with 24 additions and 99 deletions

View File

@@ -15,7 +15,6 @@ export const highlighter = await createHighlighter({
'html', 'html',
'markdown', 'markdown',
'mdx', 'mdx',
'nginx',
'bash', 'bash',
'svelte', 'svelte',
'yaml', 'yaml',

View File

@@ -6,7 +6,7 @@
"about": { "about": {
"h": "About me", "h": "About me",
"text": "<p>Fullstack developer from St. Petersburg.</p> <p>I&nbsp;like to&nbsp;explore new technologies, find unusual approaches to&nbsp;solving problems and work at&nbsp;the intersection of&nbsp;fields.</p>", "text": "<p>Fullstack developer from St. Petersburg.</p> <p>I&nbsp;like to&nbsp;explore new technologies, find unusual approaches to&nbsp;solving problems and work at&nbsp;the intersection of&nbsp;fields.</p>",
"career_value": "Yandex", "career_value": "Selectel",
"masters_value": "ITMO" "masters_value": "ITMO"
}, },
"projects": { "projects": {

View File

@@ -6,7 +6,7 @@
"about": { "about": {
"h": "Обо мне", "h": "Обо мне",
"text": "<p>Fullstack разработчик из&nbsp;Санкт-Петербурга.</p> <p>Люблю разбираться в&nbsp;новых технологиях, находить необычные подходы к&nbsp;решению проблем и&nbsp;работать на&nbsp;пересечении областей.</p>", "text": "<p>Fullstack разработчик из&nbsp;Санкт-Петербурга.</p> <p>Люблю разбираться в&nbsp;новых технологиях, находить необычные подходы к&nbsp;решению проблем и&nbsp;работать на&nbsp;пересечении областей.</p>",
"career_value": "Яндекс", "career_value": "Selectel",
"masters_value": "ИТМО" "masters_value": "ИТМО"
}, },
"projects": { "projects": {

View File

@@ -1,7 +1,7 @@
import {loadTranslations} from '$lib/translations'; import {loadTranslations} from '$lib/translations';
import type {LayoutLoad} from "./$types"; import type {LayoutLoad} from "./$types";
export const load: LayoutLoad = async ({ url, data }) => { export const load: LayoutLoad = async ({ data }) => {
const { locale } = data.i18n const { locale } = data.i18n
await loadTranslations(locale); await loadTranslations(locale);

View File

@@ -38,7 +38,7 @@ export const projects = [
], ],
links: [ links: [
{ {
href: 'https://pure-city.kopyl.dev', href: 'https://pure-city.ru',
textSlug: 'projects.link_types.website', textSlug: 'projects.link_types.website',
icon: MaterialSymbolsGlobe icon: MaterialSymbolsGlobe
} }
@@ -73,7 +73,7 @@ export const projects = [
], ],
links: [ links: [
{ {
href: 'https://tpk-artel.kopyl.dev', href: 'https://tpk-artel.ru',
textSlug: 'projects.link_types.website', textSlug: 'projects.link_types.website',
icon: MaterialSymbolsGlobe icon: MaterialSymbolsGlobe
} }

View File

@@ -1,12 +1,12 @@
import {metadata as htmlInCssMetadata} from "./html-in-css/+page.svx" import {metadata as htmlInCssMetadata} from "./html-in-css/+page.svx"
import {metadata as thisBlogMetadata} from "./this-blog/+page.svx" import {metadata as thisBlogMetadata} from "./this-blog/+page.svx"
import {metadata as shortsMetadata} from "./shorts/+page.svx" import {metadata as shortsMetadata} from "./shorts/+page.svx"
import {metadata as minecraftServerMetadata} from "./minecraft-server/+page.svx" import {metadata as vuePlusPayloadMetadata} from "./vue-plus-payload/+page.svx"
const posts = [ const posts = [
{ {
href: '/blog/minecraft-server', href: '/blog/vue-plus-payload',
...minecraftServerMetadata ...vuePlusPayloadMetadata
}, },
{ {
href: '/blog/shorts', href: '/blog/shorts',

View File

@@ -1,89 +0,0 @@
---
title: "Мой овердофига усложненный Майнкрафт сервер"
date: "2026-03-13"
---
<script>
import ArticleTitle from "$lib/components/ArticleTitle.svelte"
</script>
<ArticleTitle metadata={metadata}/>
Каждый год в моей компании друзей наступает «неделя Майнкрафта» — время, когда мы все собираемся вместе поиграть. И, как обычно, мне выпала участь поднять сервер для всей компашки.
Идею поиграть с модами обсуждали давно, но модифицированный сервер всегда оказывался слишком большой морокой. В первую очередь из-за необходимости объяснять каждому участнику, как установить все нужные моды. Была и другая проблема — постоянный риск гриферства со стороны случайных игроков. Большинство моих друзей пираты, поэтому использовать белый список не было вариантом.
К счастью, я наткнулся на [Drasl](https://github.com/unmojang/drasl)! Это открытие затянуло меня в кроличью нору и в итоге привело к той серверной инфраструктуре, которой мы пользуемся сейчас.
Некоторые из вас возможно знают, что система аутентификации, которую Mojang использовала до перехода в Microsoft, называлась Yggdrasil. Drasl — это open-source реализация этого алгоритма на Go. Проще говоря, он позволяет поднять собственный сервер аутентификации, на котором мои друзья могут зарегистрироваться и через который будет происходить авторизация.
Здесь стоит упомянуть, что я и так уже хощу несколько сервисов для своей компании друзей. В основном это VPN сервис, написанный на Go поверх [Pocketbase](https://pocketbase.io/). Поэтому все пользователи уже зарегистрированы в системе, с которой они знакомы и которой пользуются регулярно.
### Синхронизация пользователей
В Drasl есть административный API для управления пользователями. Я решил написать модуль для своего существующего Golang-монолита, который через этот API будет синхронизировать пользователей Minecraft с пользователями моей основной системы. Drasl я развернул во внутренней сети и сделал доступ к нему через Nginx с ограничением:
- публично доступны только Yggdrasil-эндпоинты,
- административный API может вызывать только сервер с Pocketbase.
Примерная конфигурация:
```nginx
# /etc/nginx/conf.d/mc-auth.my-domain.conf
location ^~ /drasl/ {
allow 192.168.1.0/24;
allow <pocketbase-ip>;
deny all;
proxy_pass http://192.168.1.132:25585;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location / {
proxy_pass http://192.168.1.132:25585;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
```
Впоследствии, домен, на котором размещён этот сервис, можно указать как параметр запуска для сервера и клиента Minecraft — тогда вместо официального сервера аутентификации Mojang будет использоваться кастомный.
### Что делать с клиентом?
С сервером всё просто передали параметр и ладно, но возникает вопрос: как заставить клиента использовать мой auth-сервер? Если установка модов уже была проблемой для пользователей, то эта часть выглядит ещё сложнее. Поэтому я решил форкнуть [Helios Launcher](https://github.com/dscalzi/HeliosLauncher) (ранее известный как Electron Launcher).
Я зарабатываю на хлеб фронтенд-разработкой, поэтому я обновил визуальный стиль лаунчера, привёл его к дизайну, который уже используется в веб-панели управления VPN и заменил модуль аутентификации в лаунчере на собственный.
Теперь логин проходит через мой API, который выдаёт клиенту Yggdrasil-токен, используемый для входа на сервер.
![Лаунчер](https://habrastorage.org/r/w1560/getpro/habr/upload_files/935/16f/8c1/93516f8c108692ed2e57803003e17a4c.webp)
### Автоматическая установка модов
У Helios Launcher есть ещё одна полезная возможность — он умеет автоматически скачивать и устанавливать необходимые для сервера моды, если в нем захардкодить ссылку на .json с определённой структурой. Но создание такого JSON-файла — довольно утомительный процесс, особенно если вы не используете готовый модпак с CurseForge.
Поэтому я написал небольшой хелпер-скрипт get-nebula-obj, который генерирует нужный объект для любого .jar файла с модом.
![get-nebula-obj в действии](https://habrastorage.org/r/w1560/getpro/habr/upload_files/8f9/466/2c8/8f94662c8cc0a751b8549db7e496c668.webp)
После того как я перебрал примерно 200 модов и собрал модпак, который меня устраивает, проект можно было почти считать завершённым.
### Панель управления Minecraft
Поскольку у меня уже была веб-панель для сервисов, я решил докинуть туда страницу управления Minecraft-аккаунтом. Там игроки могут: менять скин, добавлять плащ и менять свой никнейм.
![Панель управления в браузере](https://habrastorage.org/r/w1560/getpro/habr/upload_files/c61/59e/835/c6159e835101fdd80bd22c225cf08be0.webp)
Теперь игроку нужно сделать всего одну вещь: скачать мой лаунчер. После этого всё настраивается автоматически: моды, сервер в списке multiplayer, голосовой чат, шейдеры, текстурпаки.
Любой клиент, который попытается подключиться к серверу не через мой лаунчер, получит ошибку аутентификации. Судя по логам, это уже помогло отфильтровать большое количество ботов.
На разработку ушло около месяца (действие происходило до всеобъемлющего перехода на вайбкодинг). Так что неделя Майнкрафта давно позади. Следующая задача — уговорить друзей поиграть с собой в Майнкрафт.

View File

@@ -0,0 +1,15 @@
---
title: "Vue + Payload"
date: "2025-xx-xx"
---
<script>
import ArticleTitle from "$lib/components/ArticleTitle.svelte"
</script>
<ArticleTitle metadata={metadata}/>
Я разработал интеграцию между Vue и Payload CMS, превращающую последнюю в конструктор сайтов использующий готовые компоненты
Vue, которые есть в существующем проекте.
Допустим вы уже построили сайт, на котором сверстаны компоненты, они используются

View File

@@ -13,7 +13,7 @@
}, },
"include": [ "include": [
"*.config.ts", "*.config.ts",
"src/blog/**/*.svx", "src/routes/blog/**/*.svx",
"src/**/*.svelte", "src/**/*.svelte",
"src/**/*.ts", "src/**/*.ts",
"rehype/**/*.ts" "rehype/**/*.ts"