server.go 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. package server
  2. import (
  3. // "io"
  4. "net"
  5. // "errors"
  6. "fmt"
  7. "math/rand"
  8. "os"
  9. "path/filepath"
  10. "time"
  11. "github.com/beoran/woe/monolog"
  12. "github.com/beoran/woe/world"
  13. )
  14. var MSSP map[string]string
  15. const STATUS_OK = 0
  16. const STATUS_CANNOT_LISTEN = 1
  17. const STATUS_RESTART = 0
  18. const STATUS_SHUTDOWN = 4
  19. const MAX_CLIENTS = 1000
  20. func init() {
  21. MSSP = map[string]string{
  22. "NAME": "Workers Of Eruta",
  23. "UPTIME": string(time.Now().Unix()),
  24. "PLAYERS": "0",
  25. "CRAWL DELAY": "0",
  26. "CODEBASE": "WOE",
  27. "CONTACT": "beoran@gmail.com",
  28. "CREATED": "2015",
  29. "ICON": "None",
  30. "LANGUAGE": "English",
  31. "LOCATION": "USA",
  32. "MINIMUM AGE": "18",
  33. "WEBSITE": "beoran.net",
  34. "FAMILY": "Custom",
  35. "GENRE": "Science Fiction",
  36. "GAMEPLAY": "Adventure",
  37. "STATUS": "Alpha",
  38. "GAMESYSTEM": "Custom",
  39. "INTERMUD": "",
  40. "SUBGENRE": "None",
  41. "AREAS": "0",
  42. "HELPFILES": "0",
  43. "MOBILES": "0",
  44. "OBJECTS": "0",
  45. "ROOMS": "1",
  46. "CLASSES": "0",
  47. "LEVELS": "0",
  48. "RACES": "3",
  49. "SKILLS": "900",
  50. "ANSI": "1",
  51. "MCCP": "1",
  52. "MCP": "0",
  53. "MSDP": "0",
  54. "MSP": "0",
  55. "MXP": "0",
  56. "PUEBLO": "0",
  57. "UTF-8": "1",
  58. "VT100": "1",
  59. "XTERM 255 COLORS": "1",
  60. "PAY TO PLAY": "0",
  61. "PAY FOR PERKS": "0",
  62. "HIRING BUILDERS": "0",
  63. "HIRING CODERS": "0"}
  64. }
  65. type Server struct {
  66. address string
  67. listener net.Listener
  68. clients map[int]*Client
  69. tickers map[string]*Ticker
  70. alive bool
  71. World *world.World
  72. exitstatus int
  73. }
  74. type Ticker struct {
  75. *time.Ticker
  76. Server *Server
  77. Name string
  78. Milliseconds int
  79. callback func(me *Ticker, t time.Time) (stop bool)
  80. }
  81. const DEFAULT_MOTD_OK = `
  82. ###############################
  83. # Workers Of Eruta #
  84. ###############################
  85. `
  86. const DEFAULT_MOTD = `
  87. Welcome!
  88. `
  89. func (me *Server) SetupWorld() (err error) {
  90. me.World, err = world.LoadWorld(me.DataPath(), "WOE")
  91. if err != nil {
  92. monolog.Error("Could not load world WOE: %s", err)
  93. return err
  94. }
  95. if me.World == nil {
  96. monolog.Info("Creating new default world...")
  97. me.World = world.NewWorld("WOE", DEFAULT_MOTD, me.DataPath())
  98. err := me.World.Save(me.DataPath())
  99. if err != nil {
  100. monolog.Error("Could not save world: %v", err)
  101. return err
  102. } else {
  103. monolog.Info("Saved default world.")
  104. }
  105. }
  106. return nil
  107. }
  108. func NewServer(address string) (server *Server, err error) {
  109. listener, err := net.Listen("tcp", address)
  110. if err != nil {
  111. return nil, err
  112. }
  113. monolog.Info("Server listening on %s.", address)
  114. clients := make(map[int]*Client)
  115. tickers := make(map[string]*Ticker)
  116. server = &Server{address, listener, clients, tickers, true, nil, STATUS_RESTART}
  117. err = server.SetupWorld()
  118. if err != nil {
  119. monolog.Error("Could not set up or load world!")
  120. return nil, err
  121. }
  122. monolog.Info("Server world set up.")
  123. server.AddDefaultTickers()
  124. monolog.Info("Tickers set up.")
  125. return server, err
  126. }
  127. func NewTicker(server *Server, name string, milliseconds int, callback func(me *Ticker, t time.Time) bool) *Ticker {
  128. ticker := time.NewTicker(time.Millisecond * time.Duration(milliseconds))
  129. return &Ticker{ticker, server, name, milliseconds, callback}
  130. }
  131. func (me *Ticker) Run() {
  132. OUTER:
  133. for me.Server.alive {
  134. for tick := range me.C {
  135. if !me.callback(me, tick) {
  136. break OUTER
  137. }
  138. }
  139. }
  140. }
  141. func (me *Server) RemoveTicker(name string) {
  142. ticker, have := me.tickers[name]
  143. if !have {
  144. return
  145. }
  146. ticker.Stop()
  147. delete(me.tickers, name)
  148. }
  149. func (me *Server) StopTicker(name string) {
  150. ticker, have := me.tickers[name]
  151. if !have {
  152. return
  153. }
  154. ticker.Stop()
  155. }
  156. func (me *Server) AddTicker(name string, milliseconds int, callback func(me *Ticker, t time.Time) bool) *Ticker {
  157. _, have := me.tickers[name]
  158. if have {
  159. me.RemoveTicker(name)
  160. }
  161. ticker := NewTicker(me, name, milliseconds, callback)
  162. me.tickers[name] = ticker
  163. go ticker.Run()
  164. return ticker
  165. }
  166. func onWeatherTicker(me *Ticker, t time.Time) bool {
  167. me.Server.BroadcastToChannel("weather", "The weather is changing...\n")
  168. return true
  169. }
  170. func (me *Server) AddDefaultTickers() {
  171. me.AddTicker("weather", 30000, onWeatherTicker)
  172. }
  173. func (me *Server) handleDisconnectedClients() {
  174. for me.alive {
  175. time.Sleep(1)
  176. for id, client := range me.clients {
  177. if !client.IsAlive() {
  178. monolog.Info("Client %d has disconnected.", client.id)
  179. client.Close()
  180. delete(me.clients, id)
  181. }
  182. }
  183. }
  184. }
  185. func (me *Server) findFreeID() (id int, err error) {
  186. for id = 0; id < MAX_CLIENTS; id++ {
  187. client, have := me.clients[id]
  188. if (!have) || (client == nil) {
  189. return id, nil
  190. }
  191. }
  192. return -1, fmt.Errorf("Too many clients!")
  193. }
  194. func (me *Server) onConnect(conn net.Conn) (err error) {
  195. id, err := me.findFreeID()
  196. if err != nil {
  197. monolog.Info("Refusing connection for %s: too many clients. ", conn.RemoteAddr().String())
  198. conn.Close()
  199. return nil
  200. }
  201. monolog.Info("New client connected from %s, id %d. ", conn.RemoteAddr().String(), id)
  202. client := NewClient(me, id, conn)
  203. me.clients[id] = client
  204. return client.Serve()
  205. }
  206. func (me *Server) Shutdown() {
  207. monolog.Info("Server is going to shut down.")
  208. me.alive = false
  209. me.exitstatus = STATUS_SHUTDOWN
  210. }
  211. func (me *Server) Restart() {
  212. monolog.Info("Server is going to restart.")
  213. me.alive = false
  214. me.exitstatus = STATUS_RESTART
  215. }
  216. func (me *Server) Close() {
  217. monolog.Info("Closing server, shutting down tickers.")
  218. for name, _ := range me.tickers {
  219. me.RemoveTicker(name)
  220. }
  221. monolog.Info("Closing server, shutting down clients.")
  222. for _, client := range me.clients {
  223. if client.IsAlive() {
  224. client.Close()
  225. }
  226. }
  227. me.handleDisconnectedClients()
  228. monolog.Info("Closed server.")
  229. }
  230. func (me *Server) Serve() (status int, err error) {
  231. // Setup random seed here, or whatever
  232. rand.Seed(time.Now().UTC().UnixNano())
  233. go me.handleDisconnectedClients()
  234. for me.alive {
  235. if tcplistener, ok := me.listener.(*net.TCPListener); ok {
  236. tcplistener.SetDeadline(time.Now().Add(5 * time.Second))
  237. }
  238. conn, err := me.listener.Accept()
  239. if err != nil {
  240. if noe, ok := err.(*net.OpError); ok && noe.Timeout() {
  241. // it's a timeout. Do nothing, just listen again.
  242. // this to allow the alive flag to do it's work.
  243. } else {
  244. return STATUS_CANNOT_LISTEN, err
  245. }
  246. } else {
  247. go me.onConnect(conn)
  248. }
  249. }
  250. return me.exitstatus, nil
  251. }
  252. func (me *Server) BroadcastString(message string) {
  253. for _, client := range me.clients {
  254. if client.IsAlive() {
  255. client.WriteString(message)
  256. }
  257. }
  258. }
  259. func (me *Server) Broadcast(format string, args ...interface{}) {
  260. msg := fmt.Sprintf(format, args...)
  261. me.BroadcastString(msg)
  262. }
  263. func (me *Server) BroadcastStringToChannel(channelname string, message string) {
  264. for _, client := range me.clients {
  265. if client.IsLoginFinished() && client.IsListeningToChannel(channelname) {
  266. client.WriteString(message)
  267. }
  268. }
  269. }
  270. func (me *Server) BroadcastToChannel(channelname string, format string, args ...interface{}) {
  271. msg := fmt.Sprintf(format, args...)
  272. me.BroadcastStringToChannel(channelname, msg)
  273. }
  274. // Returns the data path of the server
  275. func (me *Server) DataPath() string {
  276. //
  277. cwd, err := os.Getwd()
  278. monolog.Debug("Current direcory: %s (%v).", cwd, err)
  279. if err != nil {
  280. cwd = "."
  281. }
  282. fp := filepath.Join(cwd, "data", "var")
  283. monolog.Debug("Data path: %s (%v). ", fp, err)
  284. return fp
  285. }
  286. // Returns the script path of the server
  287. func (me *Server) ScriptPath() string {
  288. //
  289. cwd, err := os.Getwd()
  290. if err != nil {
  291. cwd = "."
  292. }
  293. return filepath.Join(cwd, "data", "script")
  294. }