handle.clj
Branches > main > 9f21a66 > files / src / net / bowmap / protocol / handle.clj
(ns net.bowmap.protocol.handle
(:require
[net.bowmap.protocol.chunk :as chunk]
[net.bowmap.protocol.entity :as entity]
[net.bowmap.protocol.packet :as pk]
[net.bowmap.protocol.packet.clientbound :as clientbound]
[net.bowmap.protocol.packet.registry :as registry]
[net.bowmap.protocol.packet.serverbound :as serverbound]
[net.bowmap.protocol.registry :as reg]
[net.bowmap.server.gamemode :as gamemode]
[net.bowmap.server.ping :as ping]
[net.bowmap.server.player :as player]
[net.bowmap.server.position :as position]
[net.bowmap.server.server :as server]
[net.bowmap.stormbow.event :as event]
[net.bowmap.transport.connection :as connection]))
; Could eventually just be a seq of both packets and events if that would be nicer to read and write.
(defmulti interpret-packet
"Interprets one serverbound packet, returns a map of [:packets :events].
Safe to call within a transaction, but may update refs."
(fn [packet]
(::pk/id packet)))
(defmethod interpret-packet :default [_]
{})
(defmethod interpret-packet ::serverbound/status-request [_]
(let [{::ping/keys! [player-count max-players motd]} (ping/ping-server)]
{:packets [{::pk/id ::clientbound/status-response
:response {:version {:name "26.2"
:protocol 776}
:players {:online player-count
:max max-players}
:description motd
:enforcesSecureChat false}}]}))
(defmethod interpret-packet ::serverbound/ping-request [pk]
{:packets [{::pk/id ::clientbound/pong-response
:timestamp (:timestamp pk)}]})
(defmethod interpret-packet ::serverbound/login-start [{:keys! [uuid username]}]
{:events [{::event/id ::event/player-joining
::player/uuid uuid
::player/username username}]
:packets [{::pk/id ::clientbound/login-success
:profile {:uuid uuid
:username username
:properties []}
:session-id (random-uuid)}]})
(defmethod interpret-packet ::serverbound/login-acknowledged [_]
{:packets [{::pk/id ::clientbound/known-packs
;; TODO also probably good to unbundle this
:known-packs [{:namespace "minecraft"
:id "core"
:version "26.2"}]}]})
(defmethod interpret-packet ::serverbound/known-packs [_]
{:packets (conj (registry/registry-and-tag-packets)
{::pk/id ::clientbound/finish-config})})
; May want this "world" elsewhere, or have some programmatic code to generate it, or implement anvil and go from there?
(defn- build-chunk-packets []
(let [pk {::pk/id ::clientbound/chunk-data-and-update-light
:chunk-data
(let [stone {:block-count (* 16 16 16)
:fluid-count 0
:block-states {:bpe 0
:palatte 1}
:biomes {:bpe 0
:palatte 0}}
air {:block-count 0
:fluid-count 0
:block-states {:bpe 0
:palatte 0}
:biomes {:bpe 0
:palatte 0}}]
{:data (cons stone (repeat (- chunk/world-height-chunk-sections 1) air))})
:light-data {}}]
(for [x (range -16 17)
z (range -16 17)]
(assoc pk
:chunk-x x
:chunk-z z))))
(defmethod interpret-packet ::serverbound/finish-config [{:keys! [::player/uuid ::connection/connection]}]
(let [entity-id (entity/next-entity-id)]
{:events [{::event/id ::event/player-joined
::player/uuid uuid
::player/player {::player/uuid uuid
::connection/connection connection
::entity/id entity-id}}]
:packets (-> [{::pk/id ::clientbound/login
:entity-id entity-id
:hardcore? false
:dimension-names [reg/world-dimension]
:max-players server/max-player-count
:view-distance 32
:simulation-distance 32
:reduced-debug-info? false
:enable-respawn-screen? false
:do-limited-crafting? false
:dimension-type-id reg/world-dimension-id
:dimension-name reg/world-dimension
:hashed-seed 203894
:gamemode ::gamemode/adventure
:debug-world? false
:superflat-world? true
:has-death-location? false
:portal-cooldown 10
:sea-level 60
:online-mode? false
:enforce-secure-chat? true}
{::pk/id ::clientbound/game-event
:event :wait-for-chunks}]
(into (build-chunk-packets))
(conj {::pk/id ::clientbound/synchronize-player-position
:teleport-id (rand-int Integer/MAX_VALUE)
:x 0
:y 16
:z 0
:velocity-x 0
:velocity-y 0
:velocity-z 0
:yaw 0
:pitch 0
:flags #{}}))}))
(defn handle-player-update-movement-packet [{:keys! [::player/uuid] :as pk}]
{:events [{::event/id ::event/updated-player-movement
::player/uuid uuid
; Flags are currently unused
::position/position (select-keys pk [::position/x ::position/y ::position/z ::position/yaw ::position/pitch])}]})
(defmethod interpret-packet ::serverbound/set-player-rotation [pk]
(handle-player-update-movement-packet pk))
(defmethod interpret-packet ::serverbound/set-player-position-and-rotation [pk]
(handle-player-update-movement-packet pk))
(defmethod interpret-packet ::serverbound/set-player-position [pk]
(handle-player-update-movement-packet pk))
(defmethod interpret-packet ::serverbound/set-player-movement-flags [pk]
(handle-player-update-movement-packet pk))
(defmethod interpret-packet ::serverbound/chat-message [{:keys! [message ::player/uuid]}]
{:events [{::event/id ::event/player-sent-chat-message
::player/uuid uuid
:message message}]})