websocket-based lrcproto server
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

update for protobuf with options

+29 -79
+1 -1
go.mod
··· 4 4 5 5 require ( 6 6 github.com/gorilla/websocket v1.5.3 7 - github.com/rachel-mp4/lrcproto v0.0.0-20250523232424-249fb4ca623e 7 + github.com/rachel-mp4/lrcproto v0.0.0-20250527205756-58da8216f98c 8 8 google.golang.org/protobuf v1.36.6 9 9 )
+2 -2
go.sum
··· 2 2 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 3 3 github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= 4 4 github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 5 - github.com/rachel-mp4/lrcproto v0.0.0-20250523232424-249fb4ca623e h1:0rCvzEgNyWX48ZC1qh8+QhN27pTmr05oahCXcR2T7Go= 6 - github.com/rachel-mp4/lrcproto v0.0.0-20250523232424-249fb4ca623e/go.mod h1:hQzO36tQELGbkmRnUtKeM6NMU34t79ZcTlhM+MO7pHw= 5 + github.com/rachel-mp4/lrcproto v0.0.0-20250527205756-58da8216f98c h1:nOWeKeE7wph0IcwUyUBi0YBynUnAo4JW/J5DM88x4KM= 6 + github.com/rachel-mp4/lrcproto v0.0.0-20250527205756-58da8216f98c/go.mod h1:hQzO36tQELGbkmRnUtKeM6NMU34t79ZcTlhM+MO7pHw= 7 7 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 8 8 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 9 9 google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
+26 -76
server.go
··· 104 104 s.pongEvt = pe 105 105 } 106 106 107 + // Start starts a server, and returns an error if it has ever been started before 107 108 func (s *Server) Start() error { 108 109 if s.ctx != nil { 109 110 return errors.New("cannot start already started server") ··· 114 115 return nil 115 116 } 116 117 118 + // Stop stops a server if it has started, and returns an error if it is already stopped 117 119 func (s *Server) Stop() error { 118 120 if s.ctx == nil { 119 121 return nil ··· 128 130 } 129 131 } 130 132 133 + // Connected returns how many clients are currently connected to the server 131 134 func (s *Server) Connected() int { 132 135 return len(s.clients) 133 136 } 134 137 138 + // StopIfEmpty stops the server if it is empty, returning true. 135 139 func (s *Server) StopIfEmpty() bool { 136 140 if len(s.clients) == 0 { 137 141 s.Stop() ··· 143 147 func (s *Server) WSHandler() http.HandlerFunc { 144 148 return func(w http.ResponseWriter, r *http.Request) { 145 149 upgrader := &websocket.Upgrader{ 146 - Subprotocols: []string{"lrcprotov1"}, 150 + Subprotocols: []string{"lrc.v1"}, 147 151 CheckOrigin: func(r *http.Request) bool { 148 152 return true 149 153 }, 150 154 } 155 + // initialize 151 156 conn, err := upgrader.Upgrade(w, r, nil) 152 157 if err != nil { 153 158 log.Println("Upgrade failed:", err) ··· 170 175 s.clients[client] = true 171 176 s.clientsMu.Unlock() 172 177 178 + // lifetime 173 179 var wg sync.WaitGroup 174 180 wg.Add(2) 175 181 go func() { defer wg.Done(); s.wsWriter(client) }() ··· 177 183 s.logDebug("new ws connection!") 178 184 wg.Wait() 179 185 186 + // clean up 180 187 s.clientsMu.Lock() 181 188 delete(s.clients, client) 182 189 close(client.dataChan) 183 190 s.clientsMu.Unlock() 191 + 184 192 s.idmapsMu.Lock() 185 - for _, id := range client.myIDs { 193 + for _, id := range client.myIDs { // remove myself from the idToClient map 186 194 delete(s.idToClient, id) 187 195 } 188 - for mutedClient, _ := range client.muteMap { 196 + for mutedClient := range client.muteMap { // remove myself from everyone that I muted's backreference map 189 197 delete(mutedClient.mutedBy, client) 190 198 } 191 - for mutingClient, _ := range client.mutedBy { 199 + for mutingClient := range client.mutedBy { // remove myself from everyone who muted me 192 200 delete(mutingClient.muteMap, client) 193 201 } 194 202 s.idmapsMu.Unlock() 203 + 195 204 conn.Close() 196 205 s.logDebug("closed ws connection") 197 206 } ··· 293 302 client.myIDs = append(client.myIDs, newID) 294 303 newpost := "" 295 304 client.post = &newpost 296 - msg.Init.Id = newID 297 - nick := client.nick 298 - if nick != nil { 299 - msg.Init.Nick = *nick 300 - } else { 301 - msg.Init.Nick = "wanderer" 302 - } 303 - externID := client.externID 304 - if externID != nil { 305 - msg.Init.ExternalID = *externID 306 - } else { 307 - msg.Init.ExternalID = "" 308 - } 309 - color := client.color 310 - if color != nil { 311 - msg.Init.Color = *color 312 - } else { 313 - msg.Init.Color = 0xD90368 314 - } 315 - msg.Init.Echoed = false 316 - 305 + msg.Init.Id = &newID 306 + msg.Init.Nick = client.nick 307 + msg.Init.ExternalID = client.externID 308 + msg.Init.Color = client.color 309 + echoed := false 310 + msg.Init.Echoed = &echoed 317 311 if s.initChan != nil { 318 312 select { 319 313 case s.initChan <- *msg: ··· 329 323 func (s *Server) broadcastInit(msg *lrcpb.Event_Init, client *client) { 330 324 stdEvent := &lrcpb.Event{Msg: msg} 331 325 stdData, _ := proto.Marshal(stdEvent) 332 - msg.Init.Echoed = true 326 + echoed := true 327 + msg.Init.Echoed = &echoed 333 328 echoEvent := &lrcpb.Event{Msg: msg} 334 329 echoData, _ := proto.Marshal(echoEvent) 335 330 muteEvent := &lrcpb.Event{Msg: &lrcpb.Event_Mute{Mute: &lrcpb.Mute{Id: msg.Init.GetId()}}} ··· 363 358 s.idmapsMu.Lock() 364 359 s.clientToID[client] = nil 365 360 s.idmapsMu.Unlock() 366 - msg.Pub.Id = *curID 361 + msg.Pub.Id = curID 367 362 event := &lrcpb.Event{Msg: msg} 368 363 if s.pubChan != nil { 369 364 select { ··· 383 378 if curID == nil { 384 379 return 385 380 } 386 - newpost, err := insertAtUTF16Index(*client.post, msg.Insert.GetByteIndex(), msg.Insert.GetBody()) 381 + newpost, err := insertAtUTF16Index(*client.post, msg.Insert.GetUtf16Index(), msg.Insert.GetBody()) 387 382 if err != nil { 388 383 return 389 384 } 390 385 client.post = &newpost 391 - msg.Insert.Id = *curID 386 + msg.Insert.Id = curID 392 387 event := &lrcpb.Event{Msg: msg} 393 388 s.broadcast(event, client) 394 389 } ··· 421 416 if curID == nil { 422 417 return 423 418 } 424 - newPost, err := deleteBtwnUTF16Indices(*client.post, msg.Delete.GetByteStart(), msg.Delete.GetByteEnd()) 419 + newPost, err := deleteBtwnUTF16Indices(*client.post, msg.Delete.GetUtf16Start(), msg.Delete.GetUtf16End()) 425 420 if err != nil { 426 421 return 427 422 } 428 423 client.post = &newPost 429 - msg.Delete.Id = *curID 424 + msg.Delete.Id = curID 430 425 event := &lrcpb.Event{Msg: msg} 431 426 s.broadcast(event, client) 432 427 } ··· 544 539 client.dataChan <- data 545 540 } 546 541 } 547 - 548 - // func (s *Server) broadcastAll(evt []byte) { 549 - // s.clientsMu.Lock() 550 - // defer s.clientsMu.Unlock() 551 - // for client := range s.clients { 552 - // select { 553 - // case client.evtChan <- evt: 554 - // s.logDebug(fmt.Sprintf("b %x", evt)) 555 - // default: 556 - // s.log("kicked client") 557 - // if client.tcpconn != nil { 558 - // (*client.tcpconn).Close() 559 - // } 560 - // if client.wsconn != nil { 561 - // (*client.wsconn).Close() 562 - // } 563 - // delete(s.clients, client) 564 - // } 565 - // } 566 - // } 567 - 568 - // func (s *Server) broadcastInit(evt []byte, c *client, id uint32) { 569 - // bevt, eevt := events.GenServerEvent(evt, id) 570 - // s.clientsMu.Lock() 571 - // defer s.clientsMu.Unlock() 572 - // for client := range s.clients { 573 - // evtToSend := bevt 574 - // if client == c { 575 - // evtToSend = eevt 576 - // } 577 - // select { 578 - // case client.evtChan <- evtToSend: 579 - // s.logDebug(fmt.Sprintf("b %x", bevt)) 580 - // default: 581 - // s.log("kicked client") 582 - // if client.tcpconn != nil { 583 - // (*client.tcpconn).Close() 584 - // } 585 - // if client.wsconn != nil { 586 - // (*client.wsconn).Close() 587 - // } 588 - // delete(s.clients, client) 589 - // } 590 - // } 591 - // } 592 542 593 543 // logDebug debugs unless in production 594 544 func (server *Server) logDebug(s string) {