2022-01-18 03:23:29 +00:00
|
|
|
package server
|
|
|
|
|
|
|
|
import (
|
|
|
|
"sync"
|
2022-01-23 14:57:25 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// SinkName represents one of the registered sinks for a server.
|
|
|
|
type SinkName string
|
|
|
|
|
|
|
|
const (
|
|
|
|
// LogSink handles console output for game servers, including messages being
|
|
|
|
// sent via Wings to the console instance.
|
|
|
|
LogSink SinkName = "log"
|
|
|
|
// InstallSink handles installation output for a server.
|
|
|
|
InstallSink SinkName = "install"
|
2022-01-18 03:23:29 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// sinkPool represents a pool with sinks.
|
|
|
|
type sinkPool struct {
|
2022-01-23 14:57:25 +00:00
|
|
|
mu sync.RWMutex
|
2022-01-18 03:23:29 +00:00
|
|
|
sinks []chan []byte
|
|
|
|
}
|
|
|
|
|
2022-01-23 14:57:25 +00:00
|
|
|
// newSinkPool returns a new empty sinkPool. A sink pool generally lives with a
|
|
|
|
// server instance for it's full lifetime.
|
2022-01-18 03:23:29 +00:00
|
|
|
func newSinkPool() *sinkPool {
|
|
|
|
return &sinkPool{}
|
|
|
|
}
|
|
|
|
|
2022-01-23 14:57:25 +00:00
|
|
|
// On adds a channel to the sink pool instance.
|
|
|
|
func (p *sinkPool) On(c chan []byte) {
|
|
|
|
p.mu.Lock()
|
|
|
|
p.sinks = append(p.sinks, c)
|
|
|
|
p.mu.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Off removes a given channel from the sink pool. If no matching sink is found
|
|
|
|
// this function is a no-op. If a matching channel is found, it will be removed.
|
2022-01-18 03:23:29 +00:00
|
|
|
func (p *sinkPool) Off(c chan []byte) {
|
2022-01-23 14:57:25 +00:00
|
|
|
p.mu.Lock()
|
|
|
|
defer p.mu.Unlock()
|
2022-01-18 03:23:29 +00:00
|
|
|
|
|
|
|
sinks := p.sinks
|
|
|
|
for i, sink := range sinks {
|
|
|
|
if c != sink {
|
|
|
|
continue
|
|
|
|
}
|
2022-01-23 14:57:25 +00:00
|
|
|
|
|
|
|
// We need to maintain the order of the sinks in the slice we're tracking,
|
|
|
|
// so shift everything to the left, rather than changing the order of the
|
|
|
|
// elements.
|
2022-01-18 03:23:29 +00:00
|
|
|
copy(sinks[i:], sinks[i+1:])
|
|
|
|
sinks[len(sinks)-1] = nil
|
|
|
|
sinks = sinks[:len(sinks)-1]
|
|
|
|
p.sinks = sinks
|
2022-01-23 15:41:12 +00:00
|
|
|
|
|
|
|
// Avoid a panic if the sink channel is nil at this point.
|
|
|
|
if c != nil {
|
|
|
|
close(c)
|
|
|
|
}
|
2022-01-23 14:57:25 +00:00
|
|
|
|
2022-01-18 03:23:29 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-23 14:57:25 +00:00
|
|
|
// Destroy destroys the pool by removing and closing all sinks and destroying
|
|
|
|
// all of the channels that are present.
|
2022-01-18 03:23:29 +00:00
|
|
|
func (p *sinkPool) Destroy() {
|
2022-01-23 14:57:25 +00:00
|
|
|
p.mu.Lock()
|
|
|
|
defer p.mu.Unlock()
|
2022-01-18 03:23:29 +00:00
|
|
|
|
|
|
|
for _, c := range p.sinks {
|
2022-01-23 15:41:12 +00:00
|
|
|
if c != nil {
|
|
|
|
close(c)
|
|
|
|
}
|
2022-01-18 03:23:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
p.sinks = nil
|
|
|
|
}
|
|
|
|
|
2022-01-23 14:57:25 +00:00
|
|
|
// Push sends a given message to each of the channels registered in the pool.
|
|
|
|
func (p *sinkPool) Push(data []byte) {
|
|
|
|
p.mu.RLock()
|
2022-01-23 15:41:12 +00:00
|
|
|
// Attempt to send the data over to the channels. If the channel buffer is full,
|
|
|
|
// or otherwise blocked for some reason (such as being a nil channel), just discard
|
|
|
|
// the event data and move on to the next channel in the slice. If you don't
|
|
|
|
// implement the "default" on the select you'll block execution until the channel
|
|
|
|
// becomes unblocked, which is not what we want to do here.
|
2022-01-18 03:23:29 +00:00
|
|
|
for _, c := range p.sinks {
|
|
|
|
select {
|
2022-01-23 14:57:25 +00:00
|
|
|
case c <- data:
|
2022-01-23 15:41:12 +00:00
|
|
|
default:
|
2022-01-18 03:23:29 +00:00
|
|
|
}
|
|
|
|
}
|
2022-01-23 14:57:25 +00:00
|
|
|
p.mu.RUnlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sink returns the instantiated and named sink for a server. If the sink has
|
|
|
|
// not been configured yet this function will cause a panic condition.
|
|
|
|
func (s *Server) Sink(name SinkName) *sinkPool {
|
|
|
|
sink, ok := s.sinks[name]
|
|
|
|
if !ok {
|
|
|
|
s.Log().Fatalf("attempt to access nil sink: %s", name)
|
|
|
|
}
|
|
|
|
return sink
|
|
|
|
}
|
|
|
|
|
|
|
|
// DestroyAllSinks iterates over all of the sinks configured for the server and
|
|
|
|
// destroys their instances. Note that this will cause a panic if you attempt
|
|
|
|
// to call Server.Sink() again after. This function is only used when a server
|
|
|
|
// is being deleted from the system.
|
|
|
|
func (s *Server) DestroyAllSinks() {
|
|
|
|
s.Log().Info("destroying all registered sinks for server instance")
|
|
|
|
for _, sink := range s.sinks {
|
|
|
|
sink.Destroy()
|
|
|
|
}
|
2022-01-18 03:23:29 +00:00
|
|
|
}
|