diff --git a/.gitignore b/.gitignore index 0026861..279a7dc 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,6 @@ _cgo_export.* _testmain.go *.exe + +.idea +*.iml \ No newline at end of file diff --git a/doc.go b/doc.go new file mode 100644 index 0000000..ce935de --- /dev/null +++ b/doc.go @@ -0,0 +1,6 @@ +// berlin-mud project doc.go + +/* +berlin-mud document +*/ +package main diff --git a/game/level.go b/game/level.go new file mode 100644 index 0000000..cb54335 --- /dev/null +++ b/game/level.go @@ -0,0 +1,12 @@ +package game + +type Level struct { + Key string `xml:"key,attr"` + Name string `xml:"name"` + Directions []Direction `xml:"directions>direction"` +} + +type Direction struct { + Station string `xml:"station,attr"` + Direction string `xml:",chardata"` +} diff --git a/game/player.go b/game/player.go new file mode 100644 index 0000000..bd9453f --- /dev/null +++ b/game/player.go @@ -0,0 +1,12 @@ +package game + +import "encoding/xml" + +type Player struct { + XMLName xml.Name `xml:"player"` + Nickname string `xml:"nickname,attr"` + Gamename string `xml:"name"` + Position string `xml:"position,attr"` + PlayerType string `xml:"type"` + Ch chan string +} diff --git a/game/server.go b/game/server.go new file mode 100644 index 0000000..5f94dc7 --- /dev/null +++ b/game/server.go @@ -0,0 +1,110 @@ +package game + +import ( + "encoding/xml" + "path/filepath" + "os" + "io/ioutil" + "log" +) + +type Server struct { + name string + players map[string]Player + levels map[string]Level + workingdir string +} + +func NewServer(servername string, serverdir string) *Server { + return &Server{ + name: servername, + players: make(map[string]Player), + levels: make(map[string]Level), + workingdir: serverdir, + } +} + +func (s *Server) LoadLevels() error { + log.Println("Loading levels ...") + levelWalker := func(path string, info os.FileInfo, err error) error { + if info.IsDir() { + return nil + } + fileContent, fileIoErr := ioutil.ReadFile(path) + if fileIoErr != nil { + log.Printf("\n") + log.Printf("File %s could not be loaded\n", path) + log.Printf("%v",fileIoErr) + //return fileIoErr + return nil + } + level := Level{} + if xmlerr := xml.Unmarshal(fileContent, &level); xmlerr != nil { + log.Printf("\n") + log.Printf("File %s could not be Unmarshaled\n", path, xmlerr) + log.Printf("%v",xmlerr) + //return xmlerr + return nil + } + log.Printf(" loaded: %s", info.Name()) + s.addLevel(level) + return nil + } + + filepath.Walk(s.workingdir+"/static/levels/", levelWalker) + return nil +} + +func (s *Server) LoadPlayer(playerName string) bool { + playerFileName := s.workingdir+"/static/player/"+playerName+".player" + + log.Println("Loading player %s", playerFileName) + + fileContent, fileIoErr := ioutil.ReadFile(playerFileName) + if fileIoErr != nil { + log.Printf("\n") + log.Printf("File %s could not be loaded\n", playerFileName) + log.Printf("%v",fileIoErr) + //return fileIoErr + return false + } + + player := Player{} + if xmlerr := xml.Unmarshal(fileContent, &player); xmlerr != nil { + log.Printf("\n") + log.Printf("File %s could not be Unmarshaled\n", playerFileName, xmlerr) + log.Printf("%v",xmlerr) + //return xmlerr + return false + } + log.Printf(" loaded: %s", player.Gamename) + s.addPlayer(player) + + return true +} + +func (s *Server) addLevel(level Level) error { + s.levels[level.Key] = level + return nil +} + +func (s *Server) addPlayer(player Player) error { + s.players[player.Nickname] = player + return nil +} + +func (s *Server) GetPlayerByNick(nickname string) (Player, bool) { + player,ok := s.players[nickname] + return player, ok +} + +func (s *Server) GetRoom(key string) (Level, bool) { + level,ok := s.levels[key] + return level, ok +} + +func (s *Server) GetName() string { + return s.name +} + + diff --git a/main.go b/main.go new file mode 100644 index 0000000..41b6d2d --- /dev/null +++ b/main.go @@ -0,0 +1,156 @@ +package main + +import ( + "bufio" + "fmt" + "io" + "log" + "net" + "os" + "strings" + "github.com/woodworker/go-mud/game" +) + +type Client struct { + conn net.Conn + nickname string + player game.Player + ch chan string +} + +func main() { + workingdir, _ := os.Getwd() + + log.Printf("Leveldir %s", workingdir+"/static/levels/") + + server := game.NewServer("berlin-mud", workingdir) + server.LoadLevels() + log.Printf("%v", server) + + + ln, err := net.Listen("tcp", ":1337") + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + msgchan := make(chan string) + addchan := make(chan Client) + rmchan := make(chan Client) + + go handleMessages(msgchan, addchan, rmchan) + + for { + conn, err := ln.Accept() + if err != nil { + fmt.Println(err) + continue + } + + go handleConnection(conn, msgchan, addchan, rmchan, server) + } +} + +func (c Client) ReadLinesInto(ch chan<- string) { + bufc := bufio.NewReader(c.conn) + for { + line, err := bufc.ReadString('\n') + if err != nil { + break + } + ch <- fmt.Sprintf("%s: %s", c.player.Gamename, line) + } +} + +func (c Client) WriteLinesFrom(ch <-chan string) { + for msg := range ch { + _, err := io.WriteString(c.conn, msg) + if err != nil { + return + } + } +} + +func promptNick(c net.Conn, bufc *bufio.Reader) string { + io.WriteString(c, "What is your nick? ") + nick, _, _ := bufc.ReadLine() + return string(nick) +} + +func handleConnection(c net.Conn, msgchan chan<- string, addchan chan<- Client, rmchan chan<- Client, server *game.Server) { + bufc := bufio.NewReader(c) + defer c.Close() + + io.WriteString(c, fmt.Sprintf("\033[1;30;41mWelcome to the Go-Mud Server %s!\033[0m\n\r", server.GetName())) + + var nickname string + for { + nickname = promptNick(c, bufc) + ok := server.LoadPlayer(nickname) + if ok == true { + break + } + } + + player, playerLoaded := server.GetPlayerByNick(nickname) + + if !playerLoaded { + log.Println("problem getting user object") + io.WriteString(c, "Problem getting user object\n") + return + } + + client := Client{ + conn: c, + nickname: player.Nickname, + player: player, + ch: make(chan string), + } + + if strings.TrimSpace(client.nickname) == "" { + log.Println("invalid username") + io.WriteString(c, "Invalid Username\n") + return + } + + // Register user + addchan <- client + defer func() { + msgchan <- fmt.Sprintf("User %s left the chat room.\n\r", client.nickname) + log.Printf("Connection from %v closed.\n", c.RemoteAddr()) + rmchan <- client + }() + io.WriteString(c, fmt.Sprintf("Welcome, %s!\n\n\r", client.nickname)) + + location, locationLoaded:= server.GetRoom( client.player.Position ); + + if locationLoaded { + io.WriteString(c, fmt.Sprintf("You are at: \033[1;33;40m%s\033[m\n\n\r", location.Name)) + } + + msgchan <- fmt.Sprintf("New user %s has joined the chat room.\n\r", client.nickname) + + // I/O + go client.ReadLinesInto(msgchan) + client.WriteLinesFrom(client.ch) +} + +func handleMessages(msgchan <-chan string, addchan <-chan Client, rmchan <-chan Client) { + clients := make(map[net.Conn]chan<- string) + + for { + select { + case msg := <-msgchan: + log.Printf("New message: %s", msg) + for _, ch := range clients { + go func(mch chan<- string) { mch <- "\033[1;33;40m" + msg + "\033[m" }(ch) + } + case client := <-addchan: + log.Printf("New client: %v\n", client.conn) + clients[client.conn] = client.ch + case client := <-rmchan: + log.Printf("Client disconnects: %v\n", client.conn) + delete(clients, client.conn) + } + } +} diff --git a/static/levels/alex.lvl b/static/levels/alex.lvl new file mode 100644 index 0000000..2bdbb1f --- /dev/null +++ b/static/levels/alex.lvl @@ -0,0 +1,6 @@ + + Alexanderplatz + + East + + \ No newline at end of file diff --git a/static/levels/jannowitzbruecke.lvl b/static/levels/jannowitzbruecke.lvl new file mode 100644 index 0000000..4594005 --- /dev/null +++ b/static/levels/jannowitzbruecke.lvl @@ -0,0 +1,6 @@ + + Jannowitzbrücke + + West + + \ No newline at end of file diff --git a/static/player/woodworker.player b/static/player/woodworker.player new file mode 100644 index 0000000..b7a9184 --- /dev/null +++ b/static/player/woodworker.player @@ -0,0 +1,4 @@ + + The Mighty WooDWorkeR + Geek + \ No newline at end of file