9ac197d
Branches > client-handling-rewrite > 9ac197d > files / resources
Add temporary chunks for the player to stand on
Commit: 9ac197dc42de09a33cc156e6a42e5c50b70238a9
Parent: 67c943e
7 changed files:
src/net/bowmap/message_application.clj view file
@@ -2,6 +2,7 @@ (:require [clojure.alpha.spec :as s] [dev.grahp.spec :as S]+ [net.bowmap.network.chunk :as chunk] [net.bowmap.network.client :as client] [net.bowmap.network.message :as msg] [net.bowmap.network.message.client :as cl]@@ -80,35 +81,69 @@ (conj (vec registry/registry-messages) {::msg/id ::cl/finish-config})}) +(defn- build-chunk-messages []+ (let [pk {::msg/id ::cl/chunk-data-and-update-light+ :chunk-data+ (let [stone {:block-count (* 16 16 16)+ :block-states {:bpe 0+ :palatte 1}+ :biomes {:bpe 0+ :palatte 0}}+ air {:block-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 apply-message ::srv/finish-config [_ client] {::client/client (assoc client ::state/connection-state ::state/play) ::messages- [{::msg/id ::cl/login- ;; TODO magic zero :p- ;; I think you can keep a registry of entity ids and stick this on the client- :entity-id 0- :hardcore? false- :dimension-names ["world"]- :max-players 1000 ; TODO extract :p- :view-distance 32- :simulation-distance 32- :reduced-debug-info? false- :enable-respawn-screen? false- :do-limited-crafting? false- :dimension-type-id 0- :dimension-name "world"- :hashed-seed 203894- :gamemode :creative- :previous-gamemode :unknown- :debug-world? false- :superflat-world? true- :has-death-location? false- :portal-cooldown 10- :sea-level 60- :enforce-secure-chat? true}+ (-> [{::msg/id ::cl/login+ ;; TODO magic zero :p+ ;; I think you can keep a registry of entity ids and stick this on the client+ :entity-id 0+ :hardcore? false+ :dimension-names ["world"]+ :max-players 1000 ; TODO extract :p+ :view-distance 32+ :simulation-distance 32+ :reduced-debug-info? false+ :enable-respawn-screen? false+ :do-limited-crafting? false+ :dimension-type-id 0+ :dimension-name "world"+ :hashed-seed 203894+ :gamemode :creative+ :previous-gamemode :unknown+ :debug-world? false+ :superflat-world? true+ :has-death-location? false+ :portal-cooldown 10+ :sea-level 60+ :enforce-secure-chat? true} - {::msg/id ::cl/game-event- :event :wait-for-chunks}]+ {::msg/id ::cl/game-event+ :event :wait-for-chunks}]+ (into (build-chunk-messages))+ (conj {::msg/id ::cl/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 #{}}))+ ::processes [{::process-id ::keep-alive ::process-timeout 20000}]}) src/net/bowmap/minecraft_server.clj view file
@@ -26,10 +26,8 @@ Because packet reading is dependent on the state of the client, and handling a packet can update the state of the client, Reading and handling must be synchronized!" [pk client messages-out process-chan]- (if-some [msg (guard (srv/packet->message pk (::state/connection-state client)))]- (run-effects! messages-out process-chan (a/apply-message msg client))- (do (prn "Unprocessed message!" {:pk pk :client client})- client)))+ (when-some [msg (srv/packet->message pk (::state/connection-state client))]+ (run-effects! messages-out process-chan (a/apply-message msg client)))) (defn handle-process! [process client messages-out process-chan] (run-effects! messages-out process-chan (a/apply-process process client)))@@ -64,7 +62,6 @@ ; TODO make reloadable, if port is already running, just swap it out? (defn start-server! [{:keys [port]}]- ; Start the client handling loop here (let [{::server/keys [server client-chan]} (server/start-tcp-server! {:port port})] (io-thread ; Loop over the instance?src/net/bowmap/network/message/client.clj view file
@@ -7,9 +7,10 @@ [net.bowmap.network.packet :as pk] [net.bowmap.network.packet.types :as t] [net.bowmap.network.packet.write :as write- :refer [boolean-> byte-> context-optional-> enum-> float-> identifier->- int-> json-> length-prefixed list-> long-> nbt-> optional-> or->- position-> string-> table-> ubyte-> uuid-> varint->]]+ :refer [bit-mask-> boolean-> byte-> chunk-data-> context-optional->+ double-> enum-> float-> identifier-> int-> json-> length-prefixed+ light-data-> list-> long-> nbt-> optional-> or-> position->+ string-> table-> ubyte-> uuid-> varint->]] [net.bowmap.network.state :as state]) (:import [io.netty.buffer Unpooled]))@@ -253,7 +254,25 @@ :enforce-secure-chat? boolean->] ;; And others too but we don't talk about those ::game-event [:event (enum-> byte-> {3 :set-gamemode 13 :wait-for-chunks})- :value (or-> float-> 0)]}))+ :value (or-> float-> 0)]+ ::chunk-data-and-update-light [:chunk-x int->+ :chunk-z int->+ :chunk-data chunk-data->+ :light-data light-data->]+ ::synchronize-player-position [:teleport-id varint->+ :x double->+ :y double->+ :z double->+ :velocity-x double->+ :velocity-y double->+ :velocity-z double->+ :yaw float->+ :pitch float->+ :flags (bit-mask-> int->+ [:relative-x :relative-y :relative-z+ :relative-yaw :relative-pitch+ :relative-velocity-x :relative-velocity-y :relative-velocity-z+ :rotate-velocity])]})) (defn message->packet "Encodes a message into a packet.src/net/bowmap/network/message/server.clj view file
@@ -144,6 +144,7 @@ ::client-information [] ::known-packs [] ::finish-config []+ ::confirm-teleportation [:teleport-id ->varint] ::keep-alive [:id ->long] ::client-tick-end [] ::player-loaded []@@ -155,6 +156,16 @@ :feet-y ->double :z ->double :flags ->byte]+ ::set-player-movement-flags [:flags (->bit-mask ->byte [:on-ground :pushing-against-wall])]+ ::player-command [:entity-id ->varint+ :action-id (->enum ->varint {0 :leave-bed+ 1 :start-sprinting+ 2 :stop-sprinting+ 3 :start-jumping-with-horse+ 4 :stop-jumping-with-horse+ 5 :open-vehicle-inventory+ 6 :start-flying-with-elytra})+ :jump-boost ->varint] ::set-player-position-and-rotation [:x ->double :feet-y ->double :z ->double@@ -163,11 +174,10 @@ :flags ->byte]})) (defn packet->message- ; TODO more references in docstrings- "Decodes/deserializes one packet (::net.bowmap.packet) from framed ByteBuf 'buf'.+ "Deserializes one (framed, serialized) packet into a message. The initial length field of the packet should be stripped from the buffer. Can throw if:- 1. No such packet with the numeric id and state exists+ 1. No such packet with the given numeric id and state exists 2. No schema exists for that packet id 3. 'buf' is not properly framed 4. The contents of 'buf' are not a valid packet"src/net/bowmap/network/packet/read.clj view file
@@ -48,14 +48,6 @@ #_(defn read-array [codec len buf]) -(defn- mapize-vector-or-something- "Returns a map of keys to vals, where vectors will map their indecies to their values."- [x]- {:pre [(or (map? x) (vector? x))]}- (if (vector? x)- (zipmap (range) x)- x))- (defn ^:prviate read-bit-mask! [buf codec mask] (let [n (codec buf)] (reduce-kv (fn [acc bit x]@@ -92,6 +84,6 @@ {} table)))) (defn ->bit-mask [reader mask]- (let [mask (mapize-vector-or-something mask)]+ (let [mask (t/mapize-vector-or-something mask)] (fn [buf] (read-bit-mask! buf reader mask))))src/net/bowmap/network/packet/types.clj view file
@@ -11,3 +11,11 @@ (s/* (s/cat ::field keyword? ::doc (s/? string?) ::codec fn?)))++(defn mapize-vector-or-something+ "Returns a map of keys to vals, where vectors will map their indecies to their values."+ [x]+ {:pre [(or (map? x) (vector? x))]}+ (if (vector? x)+ (zipmap (range) x)+ x))src/net/bowmap/network/packet/write.clj view file
@@ -13,12 +13,11 @@ (:import [io.netty.buffer ByteBuf CompositeByteBuf Unpooled] [java.nio.charset Charset]- [java.util UUID]))+ [java.util BitSet UUID])) (S/def ::writer (s/fspec :args (s/cat :buf #(instance? ByteBuf %) :msg ::msg/message) :ret #(instance? ByteBuf %))) - (defn varint-> [buf n] (loop [buf buf n n]@@ -208,6 +207,22 @@ (fn [buf x] (writer buf (m x))))) +(defn bitset-> [buf s]+ (let [bs (BitSet.)]+ (doseq [i s]+ (BitSet/.set bs i true))+ ((list-> long->) buf (BitSet/.toLongArray bs))))++(defn bit-mask-> [writer mask]+ (let [mask (t/mapize-vector-or-something mask)]+ (fn [buf m]+ (let [n (reduce (fn [acc [bit x]]+ (if (contains? m x)+ (bit-set acc bit)+ acc))+ 0 mask)]+ (writer buf n)))))+ (defn optional-> [writer] (let [writer (context-optional-> writer)] (fn [buf x]@@ -219,3 +234,48 @@ (let [table (S/ensure-conform ::t/table t)] (fn [buf m] (write-table! buf table m))))++; Chunks++; TODO eventually encode pallates and the different values+; Can probably use the qualified chunk keywords?+(def palatted-container->+ (table-> [:bpe ubyte->+ :palatte varint-> ; Only single values for now :3+ :data (array-> long->)]))++(def chunk-section->+ (table-> [:block-count short->+ :block-states palatted-container->+ :biomes palatted-container->]))++(defn- buf-bytes [buf]+ (let [size (ByteBuf/.writerIndex buf)+ arr (byte-array size)]+ (ByteBuf/.getBytes buf 0 arr)+ arr))++(defn wrapped-bytes->+ "Wraps writer in a writer that will return the list of bytes that writer encodes.+ Used by chunk data."+ [writer]+ (fn [buf x]+ (let [encode-buf (-> (Unpooled/buffer)+ (writer x))]+ ((list-> byte->) buf (buf-bytes encode-buf)))))++(def chunk-data->+ (table-> [:heightmaps (list-> byte->)+ ;; Array should be the size of chunk sections+ ;; TODO array length validation would probably be a good idea+ :data (wrapped-bytes-> (array-> chunk-section->))+ :block-entities (list-> byte->)]))++(def light-data->+ (table-> [:sky-light-mask bitset->+ :block-light-mask bitset->+ :empty-sky-light-mask bitset->+ :empty-block-light-mask bitset->+ :sky-light-arrays (list-> byte->)+ :block-light-arrays (list-> byte->)]))+