handle.clj
Branches > facts > 080b6b8 > 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.connection :as connection]
[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]
[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.logic :as logic]
[net.bowmap.server.server :as server]
[net.bowmap.stormbow.fact :as fact]
[net.bowmap.stormbow.responses :refer [handle-responses!]]))
(defmulti handle-packet
(fn [client packet]
(::pk/id packet)))
(defmethod handle-packet :default [client _]
{:client client})
(defmethod handle-packet ::serverbound/handshaking [client pk]
(let [{:keys! [intent protocol]} pk]
{:client (assoc client
::state/connection-state intent
:protocol-id protocol)}))
(defmethod handle-packet ::serverbound/status-request [client _]
{:client client
:facts [{::fact/id ::fact/server-list-requested
::client/client client}]})
(defmethod handle-packet ::serverbound/ping-request [client pk]
{:client client
:packets [{::pk/id ::clientbound/pong-response
:timestamp (:timestamp pk)}]})
(defmethod handle-packet ::serverbound/login-start [client pk]
(let [{:keys! [uuid username]} pk]
{:client (assoc client ::entity/uuid uuid)
:packets [{::pk/id ::clientbound/login-success
:profile {:uuid uuid
:username username
:properties []}
:session-id (random-uuid)}]}))
(defmethod handle-packet ::serverbound/login-acknowledged [client _]
{:client (assoc client ::state/connection-state ::state/config)
:packets [{::pk/id ::clientbound/known-packs
;; TODO also probably good to unbundle this
:known-packs [{:namespace "minecraft"
:id "core"
:version "26.2"}]}]})
; TODO how to update state here?
; Yield facts from this as well and send 'em in!
; Or well, this can be retried so just use the dosync
(defmethod handle-packet ::serverbound/known-packs [client _]
{:client client
:packets (conj (vec (registry/registry-packets))
{::pk/id ::clientbound/update-tags
:tagged-registries [{:registry "minecraft:block"
:tags [{:tag-name "bowmap:infiniburn"
:entries []}]}]}
{::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 []}]}]}
{::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 []}]}]}
{::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 handle-packet ::serverbound/finish-config [client _]
(let [entity-id (entity/next-entity-id!)
uuid (req! client ::entity/uuid)
client (assoc client
::state/connection-state ::state/play
::entity/id entity-id
::client/playing? true)
player {::entity/id entity-id
::entity/uuid uuid}]
; Bad state here, yield a fact instead, but that's later ofc
(dosync
(if (@server/players uuid)
(throw (Exception. "Player tried to join with an existing UUID"))
(commute server/players assoc uuid player)))
{:client client
: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 #{}}))
:keepalive? true}))
(defn handle!! [client-ref packet]
(dosync
; May want sync-facts vs. async-facts?
; The facts themselves shouldn't care but the handler might
(let [{:keys! [client] :keys [packets facts keepalive?]} (handle-packet @client-ref packet)]
(ref-set client-ref client)
(let [{:keys! [::connection/connection ::state/connection-state]} @client-ref]
(doseq [pk packets]
(send connection connection/send-frame-action! (clientbound/encode-packet! pk connection-state)))
; TODO re-re-add keepalive
(doseq [fact facts]
(handle-responses! (logic/handle-fact fact)))))))