server.go 7.6 KB

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