package core import ( "context" "encoding/json" "fmt" "log/slog" "relay/internal/shared" "github.com/libp2p/go-libp2p/core/network" "github.com/oklog/ulid/v2" ) // --- Room Management --- // GetRoomByID retrieves a local Room struct by its ULID func (r *Relay) GetRoomByID(id ulid.ULID) *shared.Room { if room, ok := r.LocalRooms.Get(id); ok { return room } return nil } // GetRoomByName retrieves a local Room struct by its name func (r *Relay) GetRoomByName(name string) *shared.Room { for _, room := range r.LocalRooms.Copy() { if room.Name == name { return room } } return nil } // CreateRoom creates a new local Room struct with the given name func (r *Relay) CreateRoom(name string) *shared.Room { roomID := ulid.Make() room := shared.NewRoom(name, roomID, r.ID) r.LocalRooms.Set(room.ID, room) slog.Debug("Created new local room", "room", name, "id", room.ID) return room } // DeleteRoomIfEmpty checks if a local room struct is inactive and can be removed func (r *Relay) DeleteRoomIfEmpty(room *shared.Room) { if room == nil { return } if room.Participants.Len() == 0 && r.LocalRooms.Has(room.ID) { slog.Debug("Deleting empty room without participants", "room", room.Name) r.LocalRooms.Delete(room.ID) err := room.PeerConnection.Close() if err != nil { slog.Error("Failed to close Room PeerConnection", "room", room.Name, "err", err) } } } // GetRemoteRoomByName returns room from mesh by name func (r *Relay) GetRemoteRoomByName(roomName string) *shared.RoomInfo { for _, room := range r.MeshRooms.Copy() { if room.Name == roomName && room.OwnerID != r.ID { // Make sure connection is alive if r.Host.Network().Connectedness(room.OwnerID) == network.Connected { return &room } else { slog.Debug("Removing stale peer, owns a room without connection", "room", roomName, "peer", room.OwnerID) r.onPeerDisconnected(room.OwnerID) } } } return nil } // --- State Publishing --- // publishRoomStates publishes the state of all rooms currently owned by *this* relay func (r *Relay) publishRoomStates(ctx context.Context) error { if r.pubTopicState == nil { slog.Warn("Cannot publish room states: topic is nil") return nil } var statesToPublish []shared.RoomInfo r.LocalRooms.Range(func(id ulid.ULID, room *shared.Room) bool { // Only publish state for rooms owned by this relay if room.OwnerID == r.ID { statesToPublish = append(statesToPublish, shared.RoomInfo{ ID: room.ID, Name: room.Name, OwnerID: r.ID, }) } return true // Continue iteration }) if len(statesToPublish) == 0 { return nil } data, err := json.Marshal(statesToPublish) if err != nil { return fmt.Errorf("failed to marshal local room states: %w", err) } if pubErr := r.pubTopicState.Publish(ctx, data); pubErr != nil { slog.Error("Failed to publish room states message", "err", pubErr) } return nil }