Define packets for Configuration
All checks were successful
Build & Test / nix-build (push) Successful in 1m8s

This commit is contained in:
Kodi Craft 2024-07-09 16:10:00 +02:00
parent 9291af2bc8
commit 7526ffae62
Signed by: kodi
GPG Key ID: 69D9EED60B242822
2 changed files with 234 additions and 1 deletions

View File

@ -0,0 +1,233 @@
# Amethyst - An experimental Minecraft server written in Elixir.
# Copyright (C) 2024 KodiCraft
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Amethyst.Server.Configuration do
@moduledoc """
This module contains the logic for the Configuration stage of the server.
"""
require Logger
use Amethyst.Server
alias Amethyst.Minecraft.Read
alias Amethyst.Minecraft.Write
## DESERIALIZATION
# Client Information https://wiki.vg/Protocol#Client_Information
def deserialize(0x00, data) do
{[locale, view_dist, chat_mode, chat_colors, displayed_skin_parts, main_hand, text_filtering, allow_listing], ""} =
Read.start(data) |> Read.string |> Read.byte |> Read.varint |> Read.bool |> Read.ubyte |> Read.varint |> Read.bool |> Read.bool |> Read.stop
chat_mode = case chat_mode do
0 -> :enabled
1 -> :commands_only
2 -> :hidden
_ -> raise RuntimeError, "Unknown chat mode #{chat_mode}"
end
main_hand = case main_hand do
0 -> :left
1 -> :right
_ -> raise RuntimeError, "Unknown main hand #{main_hand}"
end
{:client_information, locale, view_dist, chat_mode, chat_colors, displayed_skin_parts, main_hand, text_filtering, allow_listing}
end
# Cookie Response https://wiki.vg/Protocol#Cookie_Response_(configuration)
def deserialize(0x01, data) do
{[key, exists], rest} = Read.start(data) |> Read.string |> Read.bool |> Read.stop
if exists do
{[length], rest} = Read.start(rest) |> Read.varint |> Read.stop
{[data], _} = Read.start(rest) |> Read.raw(length) |> Read.stop
{:cookie_response, key, data}
else
{:cookie_response, key, nil}
end
end
# Serverbound Plugin Message https://wiki.vg/Protocol#Serverbound_Plugin_Message_(configuration)
def deserialize(0x02, data) do
{[channel], rest} = Read.start(data) |> Read.string |> Read.stop
{:serverbound_plugin_message, channel, rest}
end
# Acknowledge Finish Configuration https://wiki.vg/Protocol#Acknowledge_Finish_Configuration
def deserialize(0x03, "") do
{:acknowledge_finish_configuration}
end
# Serverbound Keep Alive https://wiki.vg/Protocol#Serverbound_Keep_Alive_(configuration)
def deserialize(0x04, data) do
{[id], ""} = Read.start(data) |> Read.long |> Read.stop
{:serverbound_keep_alive, id}
end
# Pong https://wiki.vg/Protocol#Pong_(configuration)
def deserialize(0x05, data) do
{[id], ""} = Read.start(data) |> Read.int |> Read.stop
{:pong, id}
end
# Resource Pack Response https://wiki.vg/Protocol#Resource_Pack_Response_(configuration)
def deserialize(0x06, data) do
{[uuid, result], ""} = Read.start(data) |> Read.uuid |> Read.varint |> Read.stop
result = case result do
0 -> :successfully_downloaded
1 -> :declined
2 -> :failed_to_download
3 -> :accepted
4 -> :downloaded
5 -> :invalid_url
6 -> :failed_to_reload
7 -> :discarded
_ -> raise RuntimeError, "Unknown resource pack response #{result}"
end
{:resource_pack_response, uuid, result}
end
# Serverbound Known Packs https://wiki.vg/Protocol#Serverbound_Known_Packs
def deserialize(0x07, data) do
{[count], rest} = Read.start(data) |> Read.varint |> Read.stop
{packs, _} = Enum.reduce(0..count, {[], rest}, fn _, {acc, rest} ->
{[namespace, id, version], rest} = Read.start(rest) |> Read.string |> Read.string |> Read.string |> Read.stop
{[{namespace, id, version} | acc], rest}
end)
{:resource_pack_stack, packs}
end
def deserialize(type, _) do
raise RuntimeError, "Got unknown packet type #{type}!"
end
## SERIALIZATION
# Cookie Request https://wiki.vg/Protocol#Cookie_Request_(configuration)
def serialize({:cookie_request, id}) do
Write.varint(0x00) <> Write.string(id)
end
# Clientbound Plugin Message https://wiki.vg/Protocol#Clientbound_Plugin_Message_(configuration)
def serialize({:clientbound_plugin_message, channel, data}) do
Write.varint(0x01) <> Write.string(channel) <> data
end
# Disconnect https://wiki.vg/Protocol#Disconnect_(configuration)
def serialize({:disconnect, reason}) do
Write.varint(0x02) <> Write.string(reason)
end
# Finish Configuration https://wiki.vg/Protocol#Finish_Configuration
def serialize({:finish_configuration}) do
Write.varint(0x03)
end
# Clientbound Keep Alive https://wiki.vg/Protocol#Clientbound_Keep_Alive_(configuration)
def serialize({:clientbound_keep_alive, id}) do
Write.varint(0x04) <> <<id::64-big-signed>>
end
# Ping https://wiki.vg/Protocol#Ping_(configuration)
def serialize({:ping, id}) do
Write.varint(0x05) <> <<id::32-big-signed>>
end
# Reset Chat https://wiki.vg/Protocol#Reset_Chat
def serialize({:reset_chat}) do
Write.varint(0x06)
end
# Registry Data https://wiki.vg/Protocol#Registry_Data
def serialize({:registry_data, _id, _entries}) do
raise ArgumentError, "Packet 'Registry Data' is not yet implemented"
end
# Remove Resource Pack https://wiki.vg/Protocol#Remove_Resource_Pack_(configuration)
def serialize({:remove_resource_pack, id}) do
if id == nil do
Write.varint(0x08) <> <<0x00>>
else
Write.varint(0x08) <> <<0x01>> <> Write.string(id)
end
end
# Add Resource Pack https://wiki.vg/Protocol#Add_Resource_Pack_(configuration)
def serialize({:add_resource_pack, id, url, hash, forced, msg}) do
Write.varint(0x09) <> Write.string(id) <> Write.string(url) <> Write.string(hash) <>
if(forced, do: <<0x01>>, else: <<0x00>>) <> if(msg == nil, do: <<0x00>>, else: <<0x01>> <> Write.string(msg))
end
# Store Cookie https://wiki.vg/Protocol#Store_Cookie_(configuration)
def serialize({:store_cookie, id, data}) do
Write.varint(0x0A) <> Write.string(id) <> Write.string(data)
end
# Transfer https://wiki.vg/Protocol#Transfer_(configuration)
def serialize({:transfer, addr, port}) do
Write.varint(0x0B) <> Write.string(addr) <> Write.varint(port)
end
# Feature Flags https://wiki.vg/Protocol#Feature_Flags
def serialize({:feature_flags, flags}) do
Write.varint(0x0C) <> Write.varint(length(flags)) <> Enum.reduce(flags, "", fn id, acc -> acc <> Write.string(id) end)
end
# Update Tags https://wiki.vg/Protocol#Update_Tags
def serialize({:update_tags, tags}) do
Write.varint(0x0D) <> Write.varint(length(tags)) <>
Enum.reduce(tags, "", &serialize_tag/2)
end
# Clientbound Known Packs https://wiki.vg/Protocol#Clientbound_Known_Packs
def serialize({:clientbound_known_packs, packs}) do
Write.varint(0x0E) <> Write.varint(length(packs)) <>
Enum.reduce(packs, "", fn {namespace, id, version}, acc -> acc <> Write.string(namespace) <> Write.string(id) <> Write.string(version) end)
end
# Custom Report Details https://wiki.vg/Protocol#Custom_Report_Details
def serialize({:custom_report_details, details}) do
Write.varint(0x0F) <> Write.varint(length(details)) <>
Enum.reduce(details, "", fn {id, data}, acc -> acc <> Write.string(id) <> Write.string(data) end)
end
# Server Links https://wiki.vg/Protocol#Server_Links_(configuration)
def serialize({:server_links, links}) do
Write.varint(0x10) <> Write.varint(length(links)) <>
Enum.reduce(links, "", fn {label, url}, acc ->
acc <> serialize_link_label(label) <> Write.string(url)
end)
end
def serialize(packet) do
raise ArgumentError, "Tried serializing unknown packet #{inspect(packet)}"
end
defp serialize_tag({id, elements}, acc) do
acc <> Write.string(id) <> Write.varint(length(elements)) <> serialize_elements(elements)
end
defp serialize_elements(elements) do
Enum.reduce(elements, "", fn {id, ids}, acc -> acc <> Write.string(id) <> Write.varint(length(ids)) <>
Enum.reduce(ids, "", fn id, acc -> acc <> Write.varint(id) end) end)
end
defp serialize_link_label(:bug_report) do
<<0x01>> <> Write.varint(0x00)
end
defp serialize_link_label(:community_guidelines) do
<<0x01>> <> Write.varint(0x01)
end
defp serialize_link_label(:support) do
<<0x01>> <> Write.varint(0x02)
end
defp serialize_link_label(:status) do
<<0x01>> <> Write.varint(0x03)
end
defp serialize_link_label(:feedback) do
<<0x01>> <> Write.varint(0x04)
end
defp serialize_link_label(:community) do
<<0x01>> <> Write.varint(0x05)
end
defp serialize_link_label(:website) do
<<0x01>> <> Write.varint(0x06)
end
defp serialize_link_label(:forums) do
<<0x01>> <> Write.varint(0x07)
end
defp serialize_link_label(:news) do
<<0x01>> <> Write.varint(0x08)
end
defp serialize_link_label(:announcements) do
<<0x01>> <> Write.varint(0x09)
end
defp serialize_link_label(other) do
<<0x00>> <> Write.string(other)
end
## HANDLING
def handle(tuple, _) do
Logger.error("Unhandled but known packet #{elem(tuple, 0)}")
end
end

View File

@ -16,7 +16,7 @@
defmodule Amethyst.Server.Login do
@moduledoc """
This module contains the logic for the Handshake stage of the server.
This module contains the logic for the Login stage of the server.
"""
require Logger
use Amethyst.Server