1- use std:: collections :: HashMap ;
1+ use std:: any :: Any ;
22use std:: net:: SocketAddr ;
33use std:: sync:: Arc ;
44use std:: time:: Duration ;
@@ -12,13 +12,15 @@ use uuid::Uuid;
1212use crate :: handler:: { NoopHandler , ServerHandler } ;
1313use crate :: protocol;
1414use crate :: room:: RoomManager ;
15+ use crate :: storage:: Storage ;
1516use crate :: types:: { ClientWire , Result , ServerConfig , ServerWire , SyncError } ;
1617use crate :: { error, info, warn} ;
1718
1819/// Tracks per-client state on the server side.
1920struct ClientState {
2021 room_id : Option < String > ,
2122 addr : SocketAddr ,
23+ metadata : Storage ,
2224}
2325
2426/// A broadcast relay game server.
@@ -74,7 +76,7 @@ impl ServerHandle {
7476 }
7577
7678 /// Create a room at runtime. Fails if the room already exists.
77- #[ inline( always ) ]
79+ #[ inline]
7880 pub fn create_room ( & self , id : & str ) -> Result < ( ) > {
7981 self . rooms . create ( id) ?;
8082 self . handler . on_room_create ( id) ;
@@ -85,7 +87,7 @@ impl ServerHandle {
8587 ///
8688 /// Soft delete: connected clients are not kicked, but their next
8789 /// broadcast or join will fail with a [`SyncError::RoomNotFound`].
88- #[ inline( always ) ]
90+ #[ inline]
8991 pub fn delete_room ( & self , id : & str ) -> bool {
9092 let existed = self . rooms . delete ( id) ;
9193 if existed {
@@ -135,24 +137,80 @@ impl ServerHandle {
135137 self . rooms . get ( room_id) . map ( |r| r. all_channel_lens ( ) )
136138 }
137139
138- /// Get a metadata value from a room.
139- pub fn get_room_meta ( & self , room_id : & str , key : & str ) -> Option < String > {
140- self . rooms . get ( room_id) ?. get_meta ( key)
140+ /// Store typed metadata on a room. Replaces any previous metadata.
141+ /// Returns `false` if the room doesn't exist.
142+ pub fn set_room_meta < T : Any + Send + Sync + ' static > ( & self , room_id : & str , value : T ) -> bool {
143+ if let Some ( room) = self . rooms . get ( room_id) {
144+ room. metadata . set ( value) ;
145+ true
146+ } else {
147+ false
148+ }
141149 }
142150
143- /// Set a metadata value on a room. Returns `false` if the room doesn't exist.
144- pub fn set_room_meta ( & self , room_id : & str , key : & str , value : & str ) -> bool {
145- if let Some ( room) = self . rooms . get ( room_id) {
146- room. set_meta ( key, value) ;
151+ /// Read room metadata via a callback.
152+ ///
153+ /// The callback receives `&T` if metadata is set and the type matches.
154+ /// Returns `None` if the room doesn't exist or has no metadata of type `T`.
155+ ///
156+ /// This avoids needing `Clone` on your metadata type.
157+ pub fn with_room_meta < T : Any + Send + Sync + ' static , R > (
158+ & self ,
159+ room_id : & str ,
160+ f : impl FnOnce ( & T ) -> R ,
161+ ) -> Option < R > {
162+ self . rooms . get ( room_id) ?. metadata . get ( f)
163+ }
164+
165+ /// Remove and return room metadata, downcasted to `T`.
166+ /// Returns `None` if the room doesn't exist or has no metadata of type `T`.
167+ pub fn take_room_meta < T : Any + Send + Sync + ' static > ( & self , room_id : & str ) -> Option < T > {
168+ self . rooms . get ( room_id) ?. metadata . take ( )
169+ }
170+
171+ /// Check if a room has metadata set.
172+ pub fn room_has_meta ( & self , room_id : & str ) -> bool {
173+ self . rooms . get ( room_id) . is_some_and ( |r| r. metadata . is_set ( ) )
174+ }
175+
176+ /// Store typed metadata on a connected client. Replaces any previous metadata.
177+ /// Returns `false` if the client doesn't exist.
178+ pub fn set_client_meta < T : Any + Send + Sync + ' static > (
179+ & self ,
180+ client_id : & Uuid ,
181+ value : T ,
182+ ) -> bool {
183+ if let Some ( state) = self . clients . get ( client_id) {
184+ state. metadata . set ( value) ;
147185 true
148186 } else {
149187 false
150188 }
151189 }
152190
153- /// Get all metadata from a room as a HashMap. Returns `None` if room doesn't exist.
154- pub fn get_room_meta_all ( & self , room_id : & str ) -> Option < HashMap < String , String > > {
155- self . rooms . get ( room_id) . map ( |r| r. get_all_meta ( ) )
191+ /// Read client metadata via a callback.
192+ ///
193+ /// The callback receives `&T` if metadata is set and the type matches.
194+ /// Returns `None` if the client doesn't exist or has no metadata of type `T`.
195+ pub fn with_client_meta < T : Any + Send + Sync + ' static , R > (
196+ & self ,
197+ client_id : & Uuid ,
198+ f : impl FnOnce ( & T ) -> R ,
199+ ) -> Option < R > {
200+ self . clients . get ( client_id) ?. metadata . get ( f)
201+ }
202+
203+ /// Remove and return client metadata, downcasted to `T`.
204+ /// Returns `None` if the client doesn't exist or has no metadata of type `T`.
205+ pub fn take_client_meta < T : Any + Send + Sync + ' static > ( & self , client_id : & Uuid ) -> Option < T > {
206+ self . clients . get ( client_id) ?. metadata . take ( )
207+ }
208+
209+ /// Check if a client has metadata set.
210+ pub fn client_has_meta ( & self , client_id : & Uuid ) -> bool {
211+ self . clients
212+ . get ( client_id)
213+ . is_some_and ( |c| c. metadata . is_set ( ) )
156214 }
157215
158216 /// Room a client is currently in. Returns `None` if not in a room or not connected.
@@ -358,6 +416,7 @@ impl Server {
358416 ClientState {
359417 room_id : None ,
360418 addr,
419+ metadata : Storage :: new ( ) ,
361420 } ,
362421 ) ;
363422
@@ -540,7 +599,7 @@ impl Server {
540599 Ok ( ( ) )
541600 }
542601
543- #[ inline]
602+ #[ inline( always ) ]
544603 async fn cleanup_client ( self : & Arc < Self > , client_id : Uuid , room_id : & str ) {
545604 let notify = ServerWire :: PlayerLeft { client_id } ;
546605 if let Some ( room) = self . rooms . get ( room_id) {
0 commit comments