(local socket (require "socket")) ;; Guide: https://www.love2d.org/wiki/Tutorial:Networking_with_UDP ;; Configuration (local config { :host "localhost" :port 9999 }) ;; Network state (local state { :socket nil :connected false ; :player-id nil ; :last-error nil }) ;; Callback functions for network events ; (local callbacks { ; :on-ready (fn []) ; :on-wait (fn []) ; :on-opponent-update (fn [data]) ; :on-error (fn [error]) ; }) (fn init [] "Initialize UDP connection to server Returns true on success, false on error" (let [udp (socket.udp)] (udp:setpeername config.host config.port) (udp:settimeout 0) ; non-blocking (set state.socket udp) (set state.connected true))) (fn send-msg [dg] (state.socket:send dg)) (fn update [dt] (let [(data msg) (state.socket:receive)] (when data (print (fennel.view {: data : msg}))))) ; (fn register [] ; "Send REGISTER message to server" ; (when state.socket ; (try ; (state.socket:send "REGISTER") ; (catch err ; (set state.last-error err) ; (when callbacks.on-error (callbacks.on-error err)))))) ; (fn split-string [str delimiter] ; "Split string by delimiter into table" ; (local result []) ; (local current "") ; (each [i 1 (length str) 1] ; (let [char (string.sub str i i)] ; (if (= char delimiter) ; (do ; (table.insert result current) ; (set current "")) ; (set current (.. current char))))) ; (table.insert result current) ; result) ; (fn parse-message [msg] ; "Parse message in format: COMMAND#arg1#arg2#..." ; (split-string msg "#")) ; (fn send-update [x y rotation battery] ; "Send player state to server in format: UPDATE#x#y#rotation#battery" ; (when state.socket ; (try ; (let [msg (.. "UPDATE#" x "#" y "#" rotation "#" battery)] ; (state.socket:send msg)) ; (catch err ; (set state.last-error err))))) ; (fn update [dt] ; "Call from love.update to process incoming messages" ; (when state.socket ; (let [(msg _src-ip _src-port) (state.socket:receivefrom)] ; (when msg ; (let [parts (parse-message msg) ; command (. parts 1)] ; (match command ; "READY_TO_PLAY" (callbacks.on-ready) ; "WAIT" (callbacks.on-wait) ; "UPDATE" (callbacks.on-opponent-update { ; :x (tonumber (. parts 2)) ; :y (tonumber (. parts 3)) ; :rotation (tonumber (. parts 4)) ; :battery (tonumber (. parts 5)) ; }) ; _ (print (.. "← Message: " msg)))))))) ; (fn set-callback [event cb] ; "Register callback for network events ; Available events: :on-ready :on-wait :on-opponent-update :on-error" ; (tset callbacks event cb)) (fn close [] "Close network connection" (when state.socket (state.socket:close) (set state.socket nil) (set state.connected false))) ; (fn is-connected [] ; "Check if currently connected to server" ; state.connected) ; (fn get-last-error [] ; "Get the last error that occurred" ; state.last-error) ;; Export public API { : init : update : state : send-msg : close ; :register register ; :send-update send-update ; :update update ; :set-callback set-callback ; :close close ; :is-connected is-connected ; :get-last-error get-last-error }