<commit_before>package TF2RconWrapper

import (
	"bytes"
	"log"
	"math/rand"
	"net"
	"strconv"
	"sync"
	"time"
)

type EventListener struct {
	PlayerConnected      func(PlayerData)
	PlayerDisconnected   func(PlayerData)
	PlayerGlobalMessage  func(PlayerData, string) // strings are chat message
	PlayerTeamMessage    func(PlayerData, string)
	PlayerSpawned        func(PlayerData, string) // string is class
	PlayerClassChanged   func(PlayerData, string) // string is new classes
	PlayerTeamChange     func(PlayerData, string) // string is new team
	PlayerKilled         func(PlayerKill)
	PlayerDamaged        func(PlayerDamage)
	PlayerHealed         func(PlayerHeal)
	PlayerKilledMedic    func(PlayerTrigger)
	PlayerUberFinished   func(PlayerData)
	PlayerBlockedCapture func(CPData, PlayerData) // cp blocked by player
	PlayerItemPickup     func(ItemPickup)
	TeamPointCapture     func(TeamData)
	TeamScoreUpdate      func(TeamData)
	GameOver             func()
	WorldRoundWin        func(string) // string is team which won
	CVarChange           func(variable string, value string)
	LogFileClosed        func()
	TournamentStarted    func()
}

type Listener struct {
	mapMu    *sync.RWMutex
	sources  map[string]*Source
	channels map[string](chan string)

	listenAddr   *net.UDPAddr
	redirectAddr string
}

type Source struct {
	Secret string
	logsMu *sync.RWMutex //protects logs
	logs   *bytes.Buffer

	handlerMu *sync.Mutex //protects handler and closed
	handler   *EventListener
	closed    bool
}

func (s *Source) Logs() *bytes.Buffer {
	return s.logs
}

// NewListener returns a new Listener
func NewListener(addr string) (*Listener, error) {
	return NewListenerAddr(addr, addr)
}

func NewListenerAddr(port, redirectAddr string) (*Listener, error) {
	addr, err := net.ResolveUDPAddr("udp", ":"+port)
	if err != nil {
		return nil, err
	}

	l := &Listener{
		mapMu:    new(sync.RWMutex),
		sources:  make(map[string]*Source),
		channels: make(map[string](chan string)),

		listenAddr:   addr,
		redirectAddr: redirectAddr,
	}

	conn, err := net.ListenUDP("udp", addr)
	if err != nil {
		return nil, err
	}

	go l.start(conn)
	return l, nil
}

func (l *Listener) RemoveSource(s *Source, m *TF2RconConnection) {
	s.handlerMu.Lock()
	s.closed = true
	s.handlerMu.Unlock()

	l.mapMu.Lock()
	delete(l.sources, s.Secret)
	l.mapMu.Unlock()

	m.StopLogRedirection(l.redirectAddr)
}

func (l *Listener) start(conn *net.UDPConn) {
	for {
		buff := make([]byte, 512)
		n, err := conn.Read(buff)
		if err != nil {
			log.Println(err)
		}

		secret, Lpos, err := getSecret(buff[0:n])
		if err != nil {
			continue
		}

		l.mapMu.RLock()
		source, ok := l.sources[secret]
		l.mapMu.RUnlock()

		if !ok {
			continue<commit_msg>add print argument to NewListener(Addr)<commit_after>package TF2RconWrapper

import (
	"bytes"
	"log"
	"math/rand"
	"net"
	"strconv"
	"sync"
	"time"
)

type EventListener struct {
	PlayerConnected      func(PlayerData)
	PlayerDisconnected   func(PlayerData)
	PlayerGlobalMessage  func(PlayerData, string) // strings are chat message
	PlayerTeamMessage    func(PlayerData, string)
	PlayerSpawned        func(PlayerData, string) // string is class
	PlayerClassChanged   func(PlayerData, string) // string is new classes
	PlayerTeamChange     func(PlayerData, string) // string is new team
	PlayerKilled         func(PlayerKill)
	PlayerDamaged        func(PlayerDamage)
	PlayerHealed         func(PlayerHeal)
	PlayerKilledMedic    func(PlayerTrigger)
	PlayerUberFinished   func(PlayerData)
	PlayerBlockedCapture func(CPData, PlayerData) // cp blocked by player
	PlayerItemPickup     func(ItemPickup)
	TeamPointCapture     func(TeamData)
	TeamScoreUpdate      func(TeamData)
	GameOver             func()
	WorldRoundWin        func(string) // string is team which won
	CVarChange           func(variable string, value string)
	LogFileClosed        func()
	TournamentStarted    func()
}

type Listener struct {
	mapMu    *sync.RWMutex
	sources  map[string]*Source
	channels map[string](chan string)

	listenAddr   *net.UDPAddr
	redirectAddr string
	print        bool
}

type Source struct {
	Secret string
	logsMu *sync.RWMutex //protects logs
	logs   *bytes.Buffer

	handlerMu *sync.Mutex //protects handler and closed
	handler   *EventListener
	closed    bool
}

func (s *Source) Logs() *bytes.Buffer {
	return s.logs
}

// NewListener returns a new Listener
func NewListener(addr string, print bool) (*Listener, error) {
	return NewListenerAddr(addr, addr, print)
}

func NewListenerAddr(port, redirectAddr string, print bool) (*Listener, error) {
	addr, err := net.ResolveUDPAddr("udp", ":"+port)
	if err != nil {
		return nil, err
	}

	l := &Listener{
		mapMu:    new(sync.RWMutex),
		sources:  make(map[string]*Source),
		channels: make(map[string](chan string)),

		listenAddr:   addr,
		redirectAddr: redirectAddr,
		print:        print,
	}

	conn, err := net.ListenUDP("udp", addr)
	if err != nil {
		return nil, err
	}

	go l.start(conn)
	return l, nil
}

func (l *Listener) RemoveSource(s *Source, m *TF2RconConnection) {
	s.handlerMu.Lock()
	s.closed = true
	s.handlerMu.Unlock()

	l.mapMu.Lock()
	delete(l.sources, s.Secret)
	l.mapMu.Unlock()

	m.StopLogRedirection(l.redirectAddr)
}

func (l *Listener) start(conn *net.UDPConn) {
	for {
		buff := make([]byte, 512)
		n, err := conn.Read(buff)
		if err != nil {
			log.Println(err)
		}

		secret, Lpos, err := getSecret(buff[0:n])
		if err != nil {
			continue
		}

		if l.print {
			log.Println(string(buff[0 : n-1]))
		}

		l.mapMu.RLock()
		source, ok := l.sources[secret]
		l.mapMu.RUnlock()

		if !ok {
			continue
{'fraction_non_alphanumeric': 0.11099195710455764, 'fraction_numerical': 0.0025022341376228774, 'mean_word_length': 3.636288318144159, 'pattern_counts': {'":': 2, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1913912', 'n_tokens_mistral': 1988, 'n_tokens_neox': 1908, 'n_words': 486}