From 0499ea30fef54efe933a9f29b3cf40a0df5f6b74 Mon Sep 17 00:00:00 2001 From: Evan Burkey Date: Thu, 17 Nov 2022 20:49:25 +0100 Subject: [PATCH] database (#4) Co-authored-by: Evan Burkey Reviewed-on: https://git.fputs.com/fputs/protohackers/pulls/4 --- cmd/budgetchat/main.go | 55 ++++++++++++++++++++++++++++++++++ cmd/budgetchat/room.go | 59 +++++++++++++++++++++++++++++++++++++ cmd/budgetchat/user.go | 27 +++++++++++++++++ cmd/unusualdatabase/main.go | 41 ++++++++++++++++++++++++++ pkg/conn/conn.go | 26 ++++++++++++++-- 5 files changed, 205 insertions(+), 3 deletions(-) create mode 100644 cmd/budgetchat/main.go create mode 100644 cmd/budgetchat/room.go create mode 100644 cmd/budgetchat/user.go create mode 100644 cmd/unusualdatabase/main.go diff --git a/cmd/budgetchat/main.go b/cmd/budgetchat/main.go new file mode 100644 index 0000000..c69ab45 --- /dev/null +++ b/cmd/budgetchat/main.go @@ -0,0 +1,55 @@ +package main + +import ( + "bufio" + "fmt" + "net" + + "protohackers/pkg/conn" +) + +func main() { + l, err := net.Listen(conn.Type, conn.Port) + if err != nil { + panic(err) + } + defer l.Close() + + fmt.Println(fmt.Sprintf("Listening on port %s", conn.Port)) + + r := createRoom() + for { + c, err := l.Accept() + if err != nil { + break + } + go handleConn(r, c) + } +} + +func handleConn(r *room, c net.Conn) { + u := newUser(c) + defer c.Close() + go u.sendMessage() + + u.write("Welcome to budgetchat! What shall I call you?") + + s := bufio.NewScanner(u.conn) + for s.Scan() { + input := s.Text() + if !u.online { + r.sendToRest(r.joinMsg(u), u) + u.write(r.connectMsg()) + r.joinUser(u) + } else { + r.sendToRest(msgWrap(input, u), u) + } + if s.Err() != nil { + return + } + } +} + +func msgWrap(msg string, u *user) string { + return fmt.Sprintf("[%s] %s", u.nick, msg) +} diff --git a/cmd/budgetchat/room.go b/cmd/budgetchat/room.go new file mode 100644 index 0000000..770e9a2 --- /dev/null +++ b/cmd/budgetchat/room.go @@ -0,0 +1,59 @@ +package main + +import ( + "fmt" + "strings" +) + +type room struct { + users []*user +} + +func createRoom() *room { + return &room{ + users: make([]*user, 16), + } +} + +func (r *room) joinUser(u *user) { + for _, usr := range r.users { + if usr.nick == u.nick { + + } + } + r.users = append(r.users, u) + u.online = true +} + +func (r *room) sendToAll(msg string) { + for _, u := range r.users { + if u.online { + u.write(msg) + } + } +} + +func (r *room) sendToRest(msg string, skip *user) { + for _, u := range r.users { + if u.online && u != skip { + u.write(msg) + } + } +} + +func (r *room) connected() (nicks []string) { + for _, u := range r.users { + if u.online { + nicks = append(nicks, u.nick) + } + } + return nicks +} + +func (r *room) joinMsg(u *user) string { + return fmt.Sprintf("* %s has joined the channel", u.nick) +} + +func (r *room) connectMsg() string { + return fmt.Sprintf("* The room contains: %s", strings.Join(r.connected(), ", ")) +} diff --git a/cmd/budgetchat/user.go b/cmd/budgetchat/user.go new file mode 100644 index 0000000..738ac3d --- /dev/null +++ b/cmd/budgetchat/user.go @@ -0,0 +1,27 @@ +package main + +import "net" + +type user struct { + conn net.Conn + nick string + online bool + output chan string +} + +func newUser(conn net.Conn) *user { + return &user{ + conn: conn, + output: make(chan string), + } +} + +func (u *user) write(s string) { + u.output <- s + "\n" +} + +func (u *user) sendMessage() { + for line := range u.output { + u.conn.Write([]byte(line)) + } +} diff --git a/cmd/unusualdatabase/main.go b/cmd/unusualdatabase/main.go new file mode 100644 index 0000000..4b74e94 --- /dev/null +++ b/cmd/unusualdatabase/main.go @@ -0,0 +1,41 @@ +package main + +import ( + "fmt" + "log" + "net" + "strings" + + "protohackers/pkg/conn" +) + +var ( + db map[string]string +) + +func init() { + db = make(map[string]string) + db["version"] = "0.1.1" +} + +func main() { + err := conn.StartSimpleUDP(handler, 1024) + if err != nil { + log.Fatalln(err) + } +} + +func handler(c *net.UDPConn, addr *net.UDPAddr, data []byte) { + if strings.Contains(string(data), "version=") { + return + } else if strings.Contains(string(data), "=") { + sp := strings.SplitN(string(data), "=", 2) + db[sp[0]] = sp[1] + } else { + out := fmt.Sprintf("%s=", string(data)) + if v, ok := db[string(data)]; ok { + out += v + } + c.WriteTo([]byte(out), addr) + } +} diff --git a/pkg/conn/conn.go b/pkg/conn/conn.go index 7f03be8..e611357 100644 --- a/pkg/conn/conn.go +++ b/pkg/conn/conn.go @@ -8,17 +8,16 @@ import ( const ( Port = ":3030" - Type = "tcp4" ) func StartSimple(handler func(conn net.Conn)) error { - l, err := net.Listen(Type, Port) + l, err := net.Listen("tcp4", Port) if err != nil { return err } defer l.Close() - fmt.Println(fmt.Sprintf("Listening on port %s", Port)) + fmt.Println(fmt.Sprintf("Listening TCP on port %s", Port)) for { c, err := l.Accept() @@ -26,9 +25,30 @@ func StartSimple(handler func(conn net.Conn)) error { return err } go handler(c) + } +} + +func StartSimpleUDP(handler func(*net.UDPConn, *net.UDPAddr, []byte), buffSize int) error { + srv, err := net.ResolveUDPAddr("udp4", Port) + if err != nil { + return err + } + + c, err := net.ListenUDP("udp4", srv) + if err != nil { + return err + } + defer c.Close() + + fmt.Println(fmt.Sprintf("Listening UDP on port %s", Port)) + + buf := make([]byte, buffSize) + for { + n, addr, err := c.ReadFromUDP(buf) if err != nil { return err } + go handler(c, addr, buf[:n]) } }