handle.clj
Branches > main > 4e3e94a > files / src / net / bowmap / network / handle.clj
(ns net.bowmap.network.handle
(:require
[net.bowmap.network.chunk :as chunk]
[net.bowmap.network.client :as client]
[net.bowmap.network.packet :as pk]
[net.bowmap.network.packets.clientbound :as clientbound]
[net.bowmap.network.packets.registry :as registry]
[net.bowmap.network.packets.serverbound :as serverbound :refer [decode-packet!]]
[net.bowmap.network.registry :as reg]
[net.bowmap.network.state :as state]
[net.bowmap.server.entity :as entity]
[net.bowmap.server.gamemode :as gamemode]
[net.bowmap.server.server :as server]))
; Handles incoming packets from clients
(defmulti handler
(fn [client packet]
(::pk/id packet)))
(defmethod handler :default [client pk]
(prn "Unhandled packet:" pk)
client)
(defmethod handler ::serverbound/handshaking [client pk]
(assoc client
::state/connection-state (req! pk :intent)
:protocol-id (req! pk :protocol)))
; TODO move to a fact and the game logic handler :)
(defmethod handler ::serverbound/status-request [client _]
(client/send-clientbound-packet client
{::pk/id ::clientbound/status-response
;; Should be un-bundled yeah.
:response {:version {:name "26.2"
:protocol 776}
:players {:online (count @server/players)
:max server/max-player-count}
; Is this a text component? Could make that interface
:description {:text "Bowmap!"}
:enforcesSecureChat false}}))
(defmethod handler ::serverbound/ping-request [client pk]
(client/send-clientbound-packet
client
{::pk/id ::clientbound/pong-response
:timestamp (:timestamp pk)}))
(defmethod handler ::serverbound/login-start [client pk]
(let [{:keys! [uuid username]} pk]
(client/send-clientbound-packet
(assoc client ::entity/uuid uuid)
{::pk/id ::clientbound/login-success
:profile {:uuid uuid
:username username
:properties []}
:session-id (random-uuid)})))
(defmethod handler ::serverbound/login-acknowledged [client _]
(client/send-clientbound-packet
(assoc client ::state/connection-state ::state/config)
{::pk/id ::clientbound/known-packs
;; TODO also probably good to unbundle this
:known-packs [{:namespace "minecraft"
:id "core"
:version "26.2"}]}))
(defmethod handler ::serverbound/known-packs [client _]
(doseq [pk (registry/registry-packets)]
(client/send-clientbound-packet client pk))
(client/send-clientbound-packet client {::pk/id ::clientbound/update-tags
:tagged-registries [{:registry "minecraft:block"
:tags [{:tag-name "minecraft:infiniburn_overworld"
:entries []}]}]})
(client/send-clientbound-packet client {::pk/id ::clientbound/update-tags
:tagged-registries [{:registry "minecraft:damage_type"
:tags [{:tag-name "minecraft:is_fire"
:entries []}
{:tag-name "minecraft:is_explosion"
:entries []}
{:tag-name "minecraft:bypasses_shield"
:entries []}]}]})
(client/send-clientbound-packet client {::pk/id ::clientbound/update-tags
:tagged-registries [{:registry "minecraft:banner_pattern"
:tags [{:tag-name "minecraft:pattern_item/bordure_indented" :entries []}
{:tag-name "minecraft:pattern_item/creeper" :entries []}
{:tag-name "minecraft:pattern_item/field_masoned" :entries []}
{:tag-name "minecraft:pattern_item/flow" :entries []}
{:tag-name "minecraft:pattern_item/flower" :entries []}
{:tag-name "minecraft:pattern_item/globe" :entries []}
{:tag-name "minecraft:pattern_item/guster" :entries []}
{:tag-name "minecraft:pattern_item/mojang" :entries []}
{:tag-name "minecraft:pattern_item/piglin" :entries []}
{:tag-name "minecraft:pattern_item/skull" :entries []}]}]})
(client/send-clientbound-packet client {::pk/id ::clientbound/finish-config}))
(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 handler ::serverbound/finish-config [client _]
(let [entity-id (entity/next-entity-id!)
client (assoc client
::state/connection-state ::state/play
::entity/id entity-id)
player {::entity/id entity-id
::entity/uuid (req! client ::entity/uuid)}]
(client/send-clientbound-packet client
{::pk/id ::clientbound/login
:entity-id entity-id
:hardcore? false
; TODO use the registry id, show that connection somehow because this is a reference to that id
: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})
(dosync
(commute server/players conj player))
(client/send-clientbound-packet client
{::pk/id ::clientbound/game-event
:event :wait-for-chunks})
(doseq [pk (build-chunk-packets)]
(client/send-clientbound-packet client pk))
(client/send-clientbound-packet client
{::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 #{}})))
; TODO keepalive
(defn process-serverbound-frame [client frame]
(let [packet (decode-packet! frame (req! client ::state/connection-state))]
; TODO make handler return packets and send them out here
(handler client packet)))