Запрос выбирает посты данного пользователя по заголовку.

Native Query

Такой же запрос можно написать на SQL, указав nativeQuery = true:

Pagination и Sorting

Можно получать результат постранично, указав в качестве параметра Pagable:

Создавать запрос страницы можно так:

Pageable pageable= PageRequest.of(0, 2, Sort.by(«title»));

Выше приведен запрос первой страницы (страницы нумеруются с 0), при этом на странице должно быть два элемента. Задано упорядочивание выборки по title. Подробнее о постраничном выводе и сортировке тут.

Формируемый SQL такой:

select post0_.id as id1_0_, post0_.text as text2_0_, post0_.title as title3_0_, post0_.user_id as user_id4_0_ from post post0_ where post0_.title like ? order by post0_.title asc limit ? select user0_.id as id1_1_0_, user0_.email as email2_1_0_, user0_.locked as locked3_1_0_, user0_.nickname as nickname4_1_0_, user0_.password as password5_1_0_, user0_.role as role6_1_0_ from user user0_ where user0_.id=?

update

Пример обновления заголовков всех постов пользователя:

I Built a QUARRY in Survival Minecraft

delete

Пример удаления всех постов пользователя:

Стоит отметить, что так посты удаляются пакетно, поскольку генерируется один SQL :

delete from post where user_id=?

Если же использовать генерируемый по имени метод, то посты удаляются по одному:

SQL отправляется свой для каждого поста:

Итоги

Исходный код есть на GitHub.

Источник: sysout.ru

Реализация Minecraft Query протокола в .Net Core

Minecraft Server Query – это простой протокол, позволяющий получить актуальную информацию о состоянии сервера путём отправки пары-тройки незамысловатых UDP-пакетов.

На вики есть подробное описание этого протокола с примерами реализации на разных языках. Однако меня поразило, насколько куцые реализации для .Net существуют на данный момент. Поискав некоторое время, я наткнулся на несколько репозиториев. Предлагаемые решения либо содержали банальные ошибки, либо имели урезанный функционал, хотя, казалось бы, куда ещё больше урезать-то.

Так было принято решение написать свою реализацию.

Скажи мне, кто ты.

Для начала, посмотрим, что из себя представляет сам протокол Minecraft Query. Согласно вики, мы имеем в распоряжении 3 вида пакетов запросов и, соотвественно, 3 вида пакетов ответа:

  • Handshake
  • BasicStatus
  • FullStatus

Первый тип пакета используется для получения ChallengeToken, необходимого для формирования других двух пакетов. Привязывается он к IP-адресу отправителя на 30 секунд. Смысловая нагрузка оставшихся двух ясна из названий.

Стоит отметить, что хотя последние два запроса отличаются друг от друга лишь выравниванием на концах пакетов, присылаемые ответы отличаются способом представления данных. Для примера, вот так выглядит ответ BasicStatus

Minecraft Query и Connect.dll — Как использовать!?

Ответ на запрос BasicStatus

А вот так – FullStatus

Ответ на запрос FullStatus

Все данные, помимо тех, что хранятся в short, представлены в big-endian. А для поля SessionId, которое постоянно в рамках одного клиент-сервер соединения, должно выполняться условие SessionId ручном» режиме.

Итак, определившись, можем уже представить, как будет выглядеть архитектура классов. Я вижу работу примерно таким образом

public static async Task DoSomething(IPAddress host, int port)

Здесь создаётся разовое соединение. Для долгоживущего потребуется проверять состояние сокета и инициализировать заново (об этом в конце статьи).

Для того, чтобы пакет отправить, его надо для начала сформировать. Этим будет заниматься класс Request.

public class Request < // Набор констант для формирования пакета private static readonly byte[] Magic = < 0xfe, 0xfd >; private static readonly byte[] Challenge = < 0x09 >; private static readonly byte[] Status = < 0x00 >; public byte[] Data < get; private set; >private Request()<> public byte RequestType => Data[2]; public static Request GetHandshakeRequest(SessionId sessionId) < var request = new Request(); // Собираем пакет var data = new List(); data.AddRange(Magic); data.AddRange(Challenge); data.AddRange(sessionId.GetBytes()); request.Data = data.ToArray(); return request; > public static Request GetBasicStatusRequest(SessionId sessionId, byte[] challengeToken) < if (challengeToken == null) < throw new ChallengeTokenIsNullException(); >var request = new Request(); var data = new List(); data.AddRange(Magic); data.AddRange(Status); data.AddRange(sessionId.GetBytes()); data.AddRange(challengeToken); request.Data = data.ToArray(); return request; > public static Request GetFullStatusRequest(SessionId sessionId, byte[] challengeToken) < if (challengeToken == null) < throw new ChallengeTokenIsNullException(); >var request = new Request(); var data = new List(); data.AddRange(Magic); data.AddRange(Status); data.AddRange(sessionId.GetBytes()); data.AddRange(challengeToken); data.AddRange(new byte[] ); // Padding request.Data = data.ToArray(); return request; > >

Читайте также:  Как в Майнкрафте сделать иссушающую бурю

Здесь всё просто. Храним все константы внутри класса и формируем пакет в трёх статических методах. Можно ещё заметить класс SessionId, который может давать как байтовое, так и строковое представление по необходимости.

Дождавшись ответа сервера, мы получаем последовательность байт, которые хотим привести к человекочитаемому виду. Для этого служит класс Response, который представляет набор «парсеров» в виде статических полей.

public static class Response < public static byte ParseType(byte[] data) < return data[0]; >// public static SessionId ParseSessionId(byte[] data) < if (data.Length < 1) throw new IncorrectPackageDataException(data); var sessionIdBytes = new byte[4]; Buffer.BlockCopy(data, 1, sessionIdBytes, 0, 4); return new SessionId(sessionIdBytes); >public static byte[] ParseHandshake(byte[] data) < if (data.Length < 5) throw new IncorrectPackageDataException(data); var response = BitConverter.GetBytes(int.Parse(Encoding.ASCII.GetString(data, 5, data.Length — 6))); if (BitConverter.IsLittleEndian) < response = response.Reverse().ToArray(); >return response; > public static ServerBasicState ParseBasicState(byte[] data) < if (data.Length (); short port = -1; data = data.Skip(5).ToArray(); // Skip Type + SessionId var stream = new MemoryStream(data); var sb = new StringBuilder(); int currentByte; int counter = 0; while ((currentByte = stream.ReadByte()) != -1) < if (counter >6) break; // Парсим нормер порта if (counter == 5) < byte[] portBuffer = ; if (!BitConverter.IsLittleEndian) portBuffer = portBuffer.Reverse().ToArray(); port = BitConverter.ToInt16(portBuffer); // Little-endian short counter++; continue; > // Парсим параметры-строки if (currentByte == 0x00) < string fieldValue = sb.ToString(); statusValues.Enqueue(fieldValue); sb.Clear(); counter++; >else sb.Append((char) currentByte); > var serverInfo = new ServerBasicState < Motd = statusValues.Dequeue(), GameType = statusValues.Dequeue(), Map = statusValues.Dequeue(), NumPlayers = int.Parse(statusValues.Dequeue()), MaxPlayers = int.Parse(statusValues.Dequeue()), HostPort = port, HostIp = statusValues.Dequeue(), >; return serverInfo; > // «Секции» пакета резделены константными последовательностями байт, // это можно испльзовать для проверки, что мы всё сделали правильно public static ServerFullState ParseFullState(byte[] data) < var statusKeyValues = new Dictionary(); var players = new List(); var buffer = new byte[256]; Stream stream = new MemoryStream(data); stream.Read(buffer, 0, 5); // Read Type + SessionID stream.Read(buffer, 0, 11); // Padding: 11 bytes constant var constant1 = new byte[] ; for (int i = 0; i < constant1.Length; i++) Debug.Assert(constant1[i] == buffer[i], «Byte mismatch at » + i + » Val :» + buffer[i]); var sb = new StringBuilder(); string lastKey = string.Empty; int currentByte; while ((currentByte = stream.ReadByte()) != -1) < if (currentByte == 0x00) < if (!string.IsNullOrEmpty(lastKey)) < statusKeyValues.Add(lastKey, sb.ToString()); lastKey = string.Empty; >else < lastKey = sb.ToString(); if (string.IsNullOrEmpty(lastKey)) break; >sb.Clear(); > else sb.Append((char) currentByte); > stream.Read(buffer, 0, 10); // Padding: 10 bytes constant var constant2 = new byte[] ; for (int i = 0; i < constant2.Length; i++) Debug.Assert(constant2[i] == buffer[i], «Byte mismatch at » + i + » Val :» + buffer[i]); while ((currentByte = stream.ReadByte()) != -1) < if (currentByte == 0x00) < var player = sb.ToString(); if (string.IsNullOrEmpty(player)) break; players.Add(player); sb.Clear(); >else sb.Append((char) currentByte); > ServerFullState fullState = new() < Motd = statusKeyValues[«hostname»], GameType = statusKeyValues[«gametype»], GameId = statusKeyValues[«game_id»], Version = statusKeyValues[«version»], Plugins = statusKeyValues[«plugins»], Map = statusKeyValues[«map»], NumPlayers = int.Parse(statusKeyValues[«numplayers»]), MaxPlayers = int.Parse(statusKeyValues[«maxplayers»]), PlayerList = players.ToArray(), HostIp = statusKeyValues[«hostip»], HostPort = int.Parse(statusKeyValues[«hostport»]), >; return fullState; > >

Таким образом мы получаем полный инструментарий для формирования пакетов, их отправки, получения ответов и извлечения из них необходимой информации.

Долгоживущие приложения на основе библиотеки

Вернёмся к том, о чем я говорил выше. Это можно реализовать таким образом. Код взят из моего нотификатора пользовательской активности. Здесь каждые 5 секунд запрашивается FullStatus, поэтому имеет смысл обновлять ChallengeToken периодически сразу после истечения предыдущего. Всего приложение имеет 2 режима работы: штатный и режим восстановления соединения.

В штатном режиме приложение по таймерам обновляет токен и запрашивает FullStatus. При обнаружении упавшего сервера/оборванного соединения/etc (5 попыток передачи) приложение переходит в режим восстановления соединения и при удачной попытке получения сообщения снова возвращается в штатный режим.

Читайте также:  Как покормить собаку в Майнкрафте на компьютере

Для начала напишем конструктор и два метода для запуска прослушивания сервера и окончания.

public StatusWatcher(string serverName, string host, int queryPort) < ServerName = serverName; _mcQuery = new McQuery(Dns.GetHostAddresses(host)[0], queryPort); _mcQuery.InitSocket(); >public async Task Unwatch() < await UpdateChallengeTokenTimer.DisposeAsync(); await UpdateServerStatusTimer.DisposeAsync(); >public async void Watch() < // Обновляем challengetoken по таймеру каждые 30 секунд UpdateChallengeTokenTimer = new Timer(async obj =>< if (!IsOnline) return; if(Debug) Console.WriteLine($»[INFO] [] Send handshake request»); try < var challengeToken = await _mcQuery.GetHandshake(); // Если всё ок, говорим, что мы в онлайне и сбрасываем счетчик попыток IsOnline = true; lock (_retryCounterLock) < RetryCounter = 0; >if(Debug) Console.WriteLine($»[INFO] [] ChallengeToken is set up: » + BitConverter.ToString(challengeToken)); > // Если что-то не так, увеличиваем счетчик неудачных попыток catch (Exception ex) < if (ex is SocketException || ex is McQueryException || ex is ChallengeTokenIsNullException) < if(Debug) Console.WriteLine($»[WARNING] [] [UpdateChallengeTokenTimer] Server doesn’t response. Try to reconnect: «); if(ex is McQueryException) Console.Error.WriteLine(ex); lock (_retryCounterLock) < RetryCounter++; if (RetryCounter >= RetryMaxCount) < RetryCounter = 0; WaitForServerAlive(); // Переходим в режим восстановления соединения >> > else < throw; >> >, null, 0, GettingChallengeTokenInterval); // По таймеру запрашиваем текущее состояние UpdateServerStatusTimer = new Timer(async obj => < if (!IsOnline) return; if(Debug) Console.WriteLine($»[INFO] [] Send full status request»); try < var response = await _mcQuery.GetFullStatus(); IsOnline = true; lock (_retryCounterLock) < RetryCounter = 0; >if(Debug) Console.WriteLine($»[INFO] [] Full status is received»); OnFullStatusUpdated?.Invoke(this, new ServerStateEventArgs(ServerName, response)); > // По аналогии с предыдущим catch (Exception ex) < if (ex is SocketException || ex is McQueryException || ex is ChallengeTokenIsNullException) < if(Debug) Console.WriteLine($»[WARNING] [] [UpdateServerStatusTimer] Server doesn’t response.

Try to reconnect: «); if(ex is McQueryException) Console.Error.WriteLine(ex); lock (_retryCounterLock) < RetryCounter++; if (RetryCounter >= RetryMaxCount) < RetryCounter = 0; WaitForServerAlive(); >> > else < throw; >> >, null, 500, GettingStatusInterval); >

Осталось только реализовать ожидание восстановления соединения. Для этого нам достаточно убедиться, что мы получили хоть какой-то ответ от сервера. Для этого мы можем воспользоваться тем же запросом хэндшейка, который не требует наличия действующего ChallengeToken.

public async void WaitForServerAlive() < if(Debug) Console.WriteLine($»[WARNING] [] Server is unavailable. Waiting for reconnection. «); // Отключаем отслеживание IsOnline = false; await Unwatch(); _mcQuery.InitSocket(); // Пересоздаём сокет Timer waitTimer = null; waitTimer = new Timer(async obj => < try < await _mcQuery.GetHandshake(); // Говорим, что можно возвращаться в штатный режим и отключаем таймер IsOnline = true; Watch(); lock (_retryCounterLock) < RetryCounter = 0; >waitTimer.Dispose(); > // Пересоздаем сокет каждые 5 (настраивается) неудачных соединений catch (SocketException) < if(Debug) Console.WriteLine($»[WARNING] [] [WaitForServerAlive] Server doesn’t response. Try to reconnect: «); lock (_retryCounterLock) < RetryCounter++; if (RetryCounter >= RetryMaxCount) < if(Debug) Console.WriteLine($»[WARNING] [] [WaitForServerAlive] Recreate socket»); RetryCounter = 0; _mcQuery.InitSocket(); > > > >, null, 500, 5000); >

UDP1: Вынес библиотеку в отдельный репозиторий.

Источник: habr.com

Помощь / FAQ

Вы можете найти ответ на свой вопрос на этой странице, а также написать в техподдержку здесь:

Также вы можете связаться с нами напрямую по почте: [email protected] .

Как добавить проект на Топкрафт?

Чтобы добавить ваш проект на Топкрафт, необходимо залогиниться через «Вконтакте», перейти в «Мои проекты», затем нажать на кнопку «Добавить проект» и заполнить форму добавления проекта. Пожалуйста, заполните как можно более подробно информацию о вашем проекте, добавьте красивый баннер, краткое и полное описание, укажите страну, ссылку на ролик в YouTube, скриншоты и другие данные.

Чем больше информации вы указываете, тем проще пользователям будет найти ваш проект как на Топкрафте, так и через поисковые системы.

Как добавить сервер?

Чтобы добавить сервер, вы должны вначале добавить проект. Переходим в «Мои проекты» >> кнопка «Управление» >> «Сервера» >> «Добавить сервер».

Читайте также:  Minecraft не могу авторизоваться на сервере

Пожалуйста, заполните как можно более подробно информацию о сервере, это поможет вашему проекту быть доступным для поиска и отображения в фильтрах.

Требования к серверу:

  • Ваш сервер должен быть онлайн на момент добавления, вы должны указать действующий IP-адрес или доменное имя.
  • Ваш сервер должен принимать TCP соединения по вашему порту сервера (по умолчанию это 25565 порт).

Убедитесь в том, что у вас в настройках сервера включена поддержка Advanced Minecraft query.

Почему не отображается онлайн сервера?

Если у вас сервера версии 1.6.4 и ниже, для отображения онлайна вам необходимо отредактировать файл server.properties и указать параметр enable-query=true.

Нужно ли мне включать Minecraft query?

TopCraft получает больше информации о вашем сервере (список плагинов, онлайн сервера и др.) с помощью Advanced Minecraft query протокола.

Если вы разрешаете Топкрафту видеть список ваших плагинов, вы получаете БОНУС (через некоторое время появится Ачивка, дающая особый бонус вашему проекту)!

Откройте на сервере файл server.properties и приведите эти строки к следующему виду:

  • enable-query=true
  • query.port=xxxxx

xxxxx — порт сервера

При выключеном query ваш онлайн не будет отображаться.

Как формируется рейтинг серверов?

Система рейтинга базируется на:

  • Голоса пользователей
  • Бонусы за Ачивки

Очки каждого проекта обновляются сразу же, как за него проголосовали. Голосование осуществляется через Вконтакте 1 раз в текущие сутки.

Что такое Ачивки?

Узнать больше о наградах (Ачивки) можно здесь.

Что такое Премиум?

Узнать что такое «Премиум» и как туда попасть можно на этой странице.

Как проверяется Uptime

Ваши сервера проверяются каждые 5-10 минут и мы сохраняем статистику вешго сервера на 3 месяца. Аптайм вашего сервера базируется на значениях, полученных во время опроса ваших серверов.

Можно ли добавить сервер с Hamachi?

Мы не поддерживаем сервера за Хамачи и подобными программами и никогда не будем делать этого.

Почему мой проект не отображается?

На Топкрафте отображаются проекты, которые соответствуют минимальным требованиям:

  • Топкрафт принимает только качественные проекты, со своим сайтом и минимум одним сервером, не зависимо от наличия собственного лаунчера.
  • На ваш сайт должен быть установлен и настроен скрипт поощрения игроков.
  • На главной странице вашего сайта должен быть установлен один из баннеров TopCraft.Ru. Взять баннер со счетчиком голосов или без можно на странице Мои проекты >> Управление >> Баннеры.

Я хочу добавить проект, но на Топкрафте нет моей страны

Добавьте страну «Другая», напишите в Техподдержку и мы обязательно добавим недостающую страну. После появления вашей страны, вы сможете сменить ее в настройках проекта.

Что такое «Проект дня»?

Проект дня — это случайно выбранный проект, который отображается на протяжении суток в меню «Сервера». Чтобы попасть в «Проект дня», убедитесь, что ваш проект имеет больше 70% uptime и у вас получены Ачивки «Обратить внимание» и «Избранный 1-го уровня».

Игрок на моем сервере представляется членом команды Топкрафт и просит OP

Время от времени мы заходим на ваши сервера но мы никогда не просим выдать нам OP или что-то еще. Если какой-либо игрок утверждает, что является членом нашей команды и просит у вас определенные преимущества, вы можете его забанить/кикнуть с сервера. Также мы никогда не спрашиваем ваши личные данные и пароли.

Мне нужна помощь по серверу, у меня что-то не работает

TopCraft.Ru является рейтингом Майнкрафт серверов и не оказывает услуги по созданию и настройке серверов. Информацию по созданию серверов, модам, плагинам, вы можете найти на https://www.spigotmc.org/, http://minecraft-ru.gamepedia.com/, https://bukkit.org/, http://rubukkit.org/ и других сайтах в интернете.

TopCraft рекомендует!

  • VictoryCraft ОБНОВЛЕНИЯ
  • LibrikMC
  • AMineGold.ru
  • Souls of World — Всем VIP!
  • Next-Mine | НОВЫЙ СЕРВЕР

FAQ

Источник: topcraft.ru