commit ed18be9fbd5160d9ba2dd69b567776f32e9bf339 Author: Nell Date: Sun Nov 2 01:25:30 2025 +0100 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..723ef36 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea \ No newline at end of file diff --git a/app/app.go b/app/app.go new file mode 100644 index 0000000..6ef31ce --- /dev/null +++ b/app/app.go @@ -0,0 +1,55 @@ +package app + +import ( + "context" + "fmt" + "os" + "os/signal" + "syscall" +) + +type App struct { +} + +func New() *App { + return &App{} +} + +func (app *App) Run() error { + // Context pour gérer l'arrêt gracieux + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // Channel pour capturer les signaux d'arrêt (Ctrl+c, etc...) + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) + + // Channel pour les erreurs + errChan := make(chan error, 1) + + // Lancer les app ici + go app.runWorker(ctx, errChan) + + fmt.Println("App started, press CTRL+C to stop...") + + select { + case err := <-errChan: + return fmt.Errorf("error in the app: %w", err) + case sig := <-sigChan: + fmt.Printf("\nSIGTERM received: %v, stopping app...\n", sig) + cancel() // Annule le context pour arrêter les goroutines + return nil + } +} + +func (app *App) runWorker(ctx context.Context, err_chan chan<- error) { + for { + select { + case <-ctx.Done(): + fmt.Println("Worker stopped") + return + default: + // Logique métier ... + } + } +} diff --git a/database/database.go b/database/database.go new file mode 100644 index 0000000..bb3aed0 --- /dev/null +++ b/database/database.go @@ -0,0 +1,65 @@ +package database + +import ( + "fmt" + "log" + + "gorm.io/driver/mysql" + "gorm.io/driver/postgres" + "gorm.io/driver/sqlite" + "gorm.io/gorm" + "gorm.io/gorm/logger" +) + +var DB *gorm.DB + +type DBConfig struct { + Driver string // "sqlite", "postgres", "mysql" + DSN string // Data Source Name +} + +// Initialize initialise la connexion à la base de données +func Initialize(config DBConfig) error { + var err error + var dialector gorm.Dialector + + // Sélection du driver approprié + switch config.Driver { + case "sqlite": + dialector = sqlite.Open(config.DSN) + case "postgres": + dialector = postgres.Open(config.DSN) + case "mysql": + dialector = mysql.Open(config.DSN) + default: + return fmt.Errorf("driver non supporté: %s", config.Driver) + } + + // Configuration de GORM + gormConfig := &gorm.Config{ + Logger: logger.Default.LogMode(logger.Info), + } + + // Connexion à la base de données + DB, err = gorm.Open(dialector, gormConfig) + if err != nil { + return fmt.Errorf("erreur lors de la connexion à la base de données: %w", err) + } + + log.Printf("Connexion à la base de données (%s) établie avec succès", config.Driver) + return nil +} + +// AutoMigrate effectue les migrations automatiques pour les modèles +func AutoMigrate(models ...interface{}) error { + return DB.AutoMigrate(models...) +} + +// Close ferme la connexion à la base de données +func Close() error { + sqlDB, err := DB.DB() + if err != nil { + return err + } + return sqlDB.Close() +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..47a2015 --- /dev/null +++ b/go.mod @@ -0,0 +1,24 @@ +module go_oxspeak_server + +go 1.25.3 + +require ( + filippo.io/edwards25519 v1.1.0 // indirect + github.com/deckarep/golang-set/v2 v2.8.0 // indirect + github.com/go-sql-driver/mysql v1.9.3 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgx/v5 v5.7.6 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/mattn/go-sqlite3 v1.14.32 // indirect + golang.org/x/crypto v0.43.0 // indirect + golang.org/x/sync v0.17.0 // indirect + golang.org/x/text v0.30.0 // indirect + gorm.io/driver/mysql v1.6.0 // indirect + gorm.io/driver/postgres v1.6.0 // indirect + gorm.io/driver/sqlite v1.6.0 // indirect + gorm.io/gorm v1.31.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..3770784 --- /dev/null +++ b/go.sum @@ -0,0 +1,43 @@ +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deckarep/golang-set/v2 v2.8.0 h1:swm0rlPCmdWn9mESxKOjWk8hXSqoxOp+ZlfuyaAdFlQ= +github.com/deckarep/golang-set/v2 v2.8.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= +github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.7.6 h1:rWQc5FwZSPX58r1OQmkuaNicxdmExaEz5A2DO2hUuTk= +github.com/jackc/pgx/v5 v5.7.6/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs= +github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= +golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= +golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/mysql v1.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg= +gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo= +gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4= +gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo= +gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ= +gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8= +gorm.io/gorm v1.31.0 h1:0VlycGreVhK7RF/Bwt51Fk8v0xLiiiFdbGDPIZQ7mJY= +gorm.io/gorm v1.31.0/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs= diff --git a/main.go b/main.go new file mode 100644 index 0000000..8da3c94 --- /dev/null +++ b/main.go @@ -0,0 +1,15 @@ +package main + +import app "go_oxspeak_server/app" + +//TIP

To run your code, right-click the code and select Run.

Alternatively, click +// the icon in the gutter and select the Run menu item from here.

+ +func main() { + + process := app.App{} + err := process.Run() + if err != nil { + return + } +} diff --git a/models/attachment.go b/models/attachment.go new file mode 100644 index 0000000..125df5a --- /dev/null +++ b/models/attachment.go @@ -0,0 +1,19 @@ +package models + +import ( + "time" + + "github.com/google/uuid" +) + +type Attachment struct { + ID uuid.UUID `gorm:"primaryKey" json:"id"` + MessageID uuid.UUID `gorm:"index;not null" json:"message_id"` + Filename string `gorm:"not null" json:"filename"` + FileSize int64 `gorm:"not null" json:"file_size"` + MimeType string `gorm:"not null" json:"mime_type"` + CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"` + + // Relation optionnelle vers le message + Message *Message `gorm:"foreignKey:MessageID" json:"message,omitempty"` +} diff --git a/models/category.go b/models/category.go new file mode 100644 index 0000000..5904b13 --- /dev/null +++ b/models/category.go @@ -0,0 +1,18 @@ +package models + +import ( + "time" + + "github.com/google/uuid" +) + +type Category struct { + ID uuid.UUID `gorm:"primaryKey" json:"id"` + ServerID uuid.UUID `gorm:"index;not null" json:"server_id"` + Name string `gorm:"not null" json:"name"` + CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"` + UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"` + + // Relation optionnelle vers le serveur + Server *Server `gorm:"foreignKey:ServerID" json:"server,omitempty"` +} diff --git a/models/channel.go b/models/channel.go new file mode 100644 index 0000000..1a5a226 --- /dev/null +++ b/models/channel.go @@ -0,0 +1,50 @@ +package models + +import ( + "time" + + "github.com/google/uuid" +) + +// #[derive(Debug, Clone, sqlx::FromRow, Serialize, Deserialize)] +// pub struct Channel { +// pub id: Uuid, +// #[sqlx(default)] +// pub server_id: Option, // Option = nullable +// #[sqlx(default)] +// pub category_id: Option, // Option = nullable +// #[sqlx(default)] +// pub position: i32, +// #[sqlx(rename = "type")] +// pub channel_type: ChannelType, +// pub name: Option, // Option = nullable +// pub created_at: DateTime, +// pub updated_at: DateTime, +// } + +type ChannelType string + +const ( + ChannelTypeText ChannelType = "text" + ChannelTypeVoice ChannelType = "voice" + ChannelTypeDM ChannelType = "dm" + // Ajoutez vos autres types ici +) + +type Channel struct { + ID uuid.UUID `gorm:"primaryKey" json:"id"` + ServerID *uuid.UUID `gorm:"index;constraint:OnDelete:CASCADE;" json:"server_id,omitempty"` // Pointeur = nullable + CategoryID *uuid.UUID `gorm:"index;constraint:OnDelete:SET NULL;" json:"category_id,omitempty"` // Pointeur = nullable + Position int32 `gorm:"default:0" json:"position"` + Type ChannelType `gorm:"not null;column:type" json:"type"` + Name *string `gorm:"" json:"name,omitempty"` // Pointeur = nullable + CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"` + UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"` + + Users []*User `gorm:"many2many:channel_users" json:"users,omitempty"` // accès direct aux users + UserLinks []ChannelUser `gorm:"foreignKey:ChannelID" json:"user_links,omitempty"` // accès aux lignes de jointure/métadonnées + + // Relations (optionnelles) + Server *Server `gorm:"foreignKey:ServerID" json:"server,omitempty"` + Category *Category `gorm:"foreignKey:CategoryID" json:"category,omitempty"` +} diff --git a/models/channel_user.go b/models/channel_user.go new file mode 100644 index 0000000..acea53c --- /dev/null +++ b/models/channel_user.go @@ -0,0 +1,19 @@ +package models + +import ( + "time" + + "github.com/google/uuid" +) + +type ChannelUser struct { + ChannelID uuid.UUID `gorm:"primaryKey" json:"channel_id"` + UserID uuid.UUID `gorm:"primaryKey" json:"user_id"` + + Role string `gorm:"default:'member'" json:"role"` + JoinedAt time.Time `gorm:"autoCreateTime" json:"joined_at"` + + // Relations pour navigation (optionnelles) + Channel *Channel `gorm:"foreignKey:ChannelID" json:"channel,omitempty"` + User *User `gorm:"foreignKey:UserID" json:"user,omitempty"` +} diff --git a/models/message.go b/models/message.go new file mode 100644 index 0000000..2af4e42 --- /dev/null +++ b/models/message.go @@ -0,0 +1,28 @@ +package models + +import ( + "time" + + "github.com/google/uuid" +) + +// #[derive(Debug, Clone, FromRow, Serialize, Deserialize)] +//pub struct Message { +// pub id: Uuid, +// pub channel_id: Uuid, +// pub user_id: Uuid, +// pub content: String, +// pub created_at: DateTime, +// pub edited_at: DateTime, +// pub reply_to_id: Option, +//} + +type Message struct { + ID uuid.UUID `gorm:"primaryKey" json:"id"` + ChannelID uuid.UUID `gorm:"index" json:"channel_id"` + UserID uuid.UUID `gorm:"index" json:"user_id"` + Content string `gorm:"not null" json:"content"` + CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"` + EditedAt *time.Time `gorm:"default:null" json:"edited_at,omitempty"` + ReplyToID *uuid.UUID `gorm:"index" json:"reply_to_id,omitempty"` +} diff --git a/models/server.go b/models/server.go new file mode 100644 index 0000000..0c55b05 --- /dev/null +++ b/models/server.go @@ -0,0 +1,14 @@ +package models + +import ( + "time" + + "github.com/google/uuid" +) + +type Server struct { + ID uuid.UUID `gorm:"primaryKey" json:"id"` + Password string `gorm:"not null" json:"-"` + CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"` + UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"` +} diff --git a/models/server_user.go b/models/server_user.go new file mode 100644 index 0000000..db6714d --- /dev/null +++ b/models/server_user.go @@ -0,0 +1,20 @@ +package models + +import ( + "time" + + "github.com/google/uuid" +) + +type ServerUser struct { + ID uuid.UUID `gorm:"primaryKey" json:"id"` + ServerID uuid.UUID `gorm:"index;not null" json:"server_id"` + UserID uuid.UUID `gorm:"index;not null" json:"user_id"` + Username *string `json:"username,omitempty"` // Option = pointeur nullable + JoinedAt time.Time `gorm:"autoCreateTime" json:"joined_at"` + UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"` + + // Relations pour navigation (optionnelles) + Server *Server `gorm:"foreignKey:ServerID" json:"server,omitempty"` + User *User `gorm:"foreignKey:UserID" json:"user,omitempty"` +} diff --git a/models/user.go b/models/user.go new file mode 100644 index 0000000..f6c12dc --- /dev/null +++ b/models/user.go @@ -0,0 +1,18 @@ +package models + +import ( + "time" + + "github.com/google/uuid" +) + +type User struct { + ID uuid.UUID `gorm:"primaryKey" json:"id"` + Username string `gorm:"not null" json:"username"` + PubKey string `gorm:"type:text;uniqueIndex;not null" json:"pub_key"` + CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"` + UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"` + + Channels []*Channel `gorm:"many2many:channel_users" json:"channels,omitempty"` + ChannelLinks []ChannelUser `gorm:"foreignKey:UserID" json:"channel_links,omitempty"` +} diff --git a/network/udp/enums.go b/network/udp/enums.go new file mode 100644 index 0000000..0c50a54 --- /dev/null +++ b/network/udp/enums.go @@ -0,0 +1,39 @@ +package udp + +import ( + //"encoding/binary" + //"errors" + //"fmt" + + "github.com/google/uuid" +) + +type PacketType uint8 + +const ( + PacketTypePing PacketType = 0 + PacketTypeConnect PacketType = 1 + PacketTypeDisconnect PacketType = 2 + PacketTypeVoiceData PacketType = 9 +) + +type Message interface { + Type() PacketType + MarshalBinary() ([]byte, error) + Size() int +} + +// MessagePing ClientPing représente un message ping client +type MessagePing struct { + MessageID uuid.UUID +} + +func (m *MessagePing) Type() PacketType { return PacketTypePing } +func (m *MessagePing) Size() int { return 17 } // 1 + 16 + +func (m *MessagePing) MarshalBinary() ([]byte, error) { + buf := make([]byte, m.Size()) + buf[0] = byte(PacketTypePing) + copy(buf[1:], m.MessageID[:]) + return buf, nil +} diff --git a/network/udp/routing_table.go b/network/udp/routing_table.go new file mode 100644 index 0000000..d5ef1d3 --- /dev/null +++ b/network/udp/routing_table.go @@ -0,0 +1,55 @@ +package udp + +import ( + "net" + "sync" + + mapset "github.com/deckarep/golang-set/v2" +) + +type RoutingTable struct { + mu sync.RWMutex + routes map[string]mapset.Set[*net.UDPAddr] +} + +func NewRoutingTable() *RoutingTable { + return &RoutingTable{ + routes: make(map[string]mapset.Set[*net.UDPAddr]), + } +} + +func (rt *RoutingTable) AddClient(channelID string, addr *net.UDPAddr) { + rt.mu.Lock() + defer rt.mu.Unlock() + + if rt.routes[channelID] == nil { + rt.routes[channelID] = mapset.NewSet[*net.UDPAddr]() + } + rt.routes[channelID].Add(addr) +} + +func (rt *RoutingTable) RemoveClient(channelID string, addr *net.UDPAddr) { + rt.mu.Lock() + defer rt.mu.Unlock() + + if clients, exists := rt.routes[channelID]; exists { + clients.Remove(addr) + if clients.Cardinality() == 0 { + delete(rt.routes, channelID) + } + } +} + +// GetClients returns the clients connected to the given channelID +// don't modify the returned set! +func (rt *RoutingTable) GetClients(channelID string) mapset.Set[*net.UDPAddr] { + rt.mu.RLock() + defer rt.mu.RUnlock() + + clients, exists := rt.routes[channelID] + if !exists { + return nil + } + + return clients +} diff --git a/network/udp/server.go b/network/udp/server.go new file mode 100644 index 0000000..cb9cf2b --- /dev/null +++ b/network/udp/server.go @@ -0,0 +1,115 @@ +package udp + +import ( + "context" + "fmt" + "net" + "runtime" + "sync" +) + +type Server struct { + bindAddr string + routingTable *RoutingTable + + conn *net.UDPConn + wg sync.WaitGroup + ctx context.Context + cancel context.CancelFunc +} + +func NewServer(bindAddr string) (*Server, error) { + ctx, cancel := context.WithCancel(context.Background()) + + return &Server{ + bindAddr: bindAddr, + ctx: ctx, + cancel: cancel, + }, nil +} + +func (s *Server) run() error { + add, err := net.ResolveUDPAddr("udp", s.bindAddr) + if err != nil { + return fmt.Errorf("cannot resolve address: %w", err) + } + + s.conn, err = net.ListenUDP("udp", add) + if err != nil { + return fmt.Errorf("cannot listen on address: %w", err) + } + + s.conn.SetReadBuffer(8 * 1024 * 1024) + s.conn.SetWriteBuffer(8 * 1024 * 1024) + fmt.Println("Listening on", s.bindAddr) + + for i := 0; i < runtime.NumCPU(); i++ { + s.wg.Add(1) + // todo : add so_reuseport option when on unix like system + go s.workerLoop(i) + } + + return nil +} + +func (s *Server) sendTo(data []byte, addr *net.UDPAddr) error { + if s.conn == nil { + return fmt.Errorf("server not started") + } + + _, err := s.conn.WriteToUDP(data, addr) + return err +} + +func (s *Server) workerLoop(id int) { + defer s.wg.Done() + + buffer := make([]byte, 1500) + fmt.Println("Worker", id, "started") + + for { + select { + case <-s.ctx.Done(): + fmt.Println("Worker", id, "stopped") + return + default: + size, addr, err := s.conn.ReadFromUDP(buffer) + if err != nil { + if s.ctx.Err() != nil { + return + } + if opErr, ok := err.(*net.OpError); ok && opErr.Temporary() { + continue + } + fmt.Printf("Error reading from UDP: %v\n", err) + continue + } + + s.handlePacket(buffer[:size], addr) + } + } +} + +func (s *Server) handlePacket(data []byte, addr *net.UDPAddr) { + pt := PacketType(data[0]) + switch pt { + case PacketTypePing: + err := s.sendTo([]byte{byte(PacketTypePing)}, addr) + if err != nil { + return + } + return + case PacketTypeConnect: + return + case PacketTypeDisconnect: + return + case PacketTypeVoiceData: + // todo : déterminer le format du packet + //channelID := string(data[1:5]) + return + default: + return + + } + +}