server.go 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. package server
  2. import (
  3. "log"
  4. // "io"
  5. "net"
  6. // "errors"
  7. "os"
  8. "math/rand"
  9. "time"
  10. "fmt"
  11. "path/filepath"
  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. logger * log.Logger
  70. logfile * os.File
  71. clients map[int] * Client
  72. tickers map[string] * Ticker
  73. alive bool
  74. World * world.World
  75. exitstatus int
  76. }
  77. type Ticker struct {
  78. * time.Ticker
  79. Server * Server
  80. Name string
  81. Milliseconds int
  82. callback func(me * Ticker, t time.Time) (stop bool)
  83. }
  84. const DEFAULT_MOTD_OK =
  85. `
  86. ###############################
  87. # Workers Of Eruta #
  88. ###############################
  89. `
  90. const DEFAULT_MOTD =
  91. `
  92. Welcome!
  93. `
  94. func (me * Server) SetupWorld() (err error) {
  95. me.World, err = world.LoadWorld(me.DataPath(), "WOE")
  96. if err != nil {
  97. monolog.Error("Could not load world WOE: %s", err)
  98. return err
  99. }
  100. if me.World == nil {
  101. monolog.Info("Creating new default world...")
  102. me.World = world.NewWorld("WOE", DEFAULT_MOTD)
  103. err := me.World.Save(me.DataPath())
  104. if err != nil {
  105. monolog.Error("Could not save world: %v", err)
  106. return err
  107. } else {
  108. monolog.Info("Saved default world.")
  109. }
  110. }
  111. return nil
  112. }
  113. func NewServer(address string) (server * Server, err error) {
  114. listener, err := net.Listen("tcp", address);
  115. if (err != nil) {
  116. return nil, err
  117. }
  118. logfile, err := os.OpenFile("log.woe", os.O_WRONLY | os.O_APPEND | os.O_CREATE, 0660)
  119. if (err != nil) {
  120. return nil, err
  121. }
  122. logger := log.New(logfile, "woe", log.Llongfile | log.LstdFlags)
  123. clients := make(map[int] * Client)
  124. tickers := make(map[string] * Ticker)
  125. server = &Server{address, listener, logger, logfile, clients, tickers, true, nil, STATUS_RESTART}
  126. err = server.SetupWorld()
  127. server.AddDefaultTickers()
  128. return server, err
  129. }
  130. func NewTicker(server * Server, name string, milliseconds int, callback func (me * Ticker, t time.Time) bool) (* Ticker) {
  131. ticker := time.NewTicker(time.Millisecond * time.Duration(milliseconds))
  132. return &Ticker {ticker, server, name, milliseconds, callback}
  133. }
  134. func (me * Ticker) Run() {
  135. OUTER:
  136. for me.Server.alive {
  137. for tick := range me.C {
  138. if (!me.callback(me, tick)) {
  139. break OUTER;
  140. }
  141. }
  142. }
  143. }
  144. func (me * Server) RemoveTicker(name string) {
  145. ticker, have := me.tickers[name]
  146. if (!have) {
  147. return
  148. }
  149. ticker.Stop()
  150. delete(me.tickers, name)
  151. }
  152. func (me * Server) StopTicker(name string) {
  153. ticker, have := me.tickers[name]
  154. if (!have) {
  155. return
  156. }
  157. ticker.Stop();
  158. }
  159. func (me * Server) AddTicker(name string, milliseconds int, callback func (me * Ticker, t time.Time) bool) (* Ticker) {
  160. _, have := me.tickers[name]
  161. if have {
  162. me.RemoveTicker(name)
  163. }
  164. ticker := NewTicker(me, name, milliseconds, callback)
  165. me.tickers[name] = ticker
  166. go ticker.Run();
  167. return ticker
  168. }
  169. func onWeatherTicker (me * Ticker, t time.Time) bool {
  170. me.Server.Broadcast("The weather is changing...\n")
  171. return true
  172. }
  173. func (me * Server) AddDefaultTickers() {
  174. me.AddTicker("weather", 30000, onWeatherTicker)
  175. }
  176. func (me * Server) handleDisconnectedClients() {
  177. for me.alive {
  178. time.Sleep(1)
  179. for id, client := range me.clients {
  180. if (!client.IsAlive()) {
  181. monolog.Info("Client %d has disconnected.", client.id)
  182. client.Close()
  183. delete(me.clients, id);
  184. }
  185. }
  186. }
  187. }
  188. func (me * Server) findFreeID() (id int, err error) {
  189. for id = 0 ; id < MAX_CLIENTS ; id++ {
  190. client, have := me.clients[id]
  191. if (!have) || (client == nil) {
  192. return id, nil
  193. }
  194. }
  195. return -1, fmt.Errorf("Too many clients!");
  196. }
  197. func (me * Server) onConnect(conn net.Conn) (err error) {
  198. id, err := me.findFreeID()
  199. if err != nil {
  200. monolog.Info("Refusing connection for %s: too many clients. ", conn.RemoteAddr().String())
  201. conn.Close()
  202. return nil
  203. }
  204. monolog.Info("New client connected from %s, id %d. ", conn.RemoteAddr().String(), id)
  205. client := NewClient(me, id, conn)
  206. me.clients[id] = client
  207. return client.Serve()
  208. }
  209. func (me * Server) Shutdown() {
  210. monolog.Info("Server is going to shut down.")
  211. me.alive = false
  212. me.exitstatus = STATUS_SHUTDOWN
  213. }
  214. func (me * Server) Restart() {
  215. monolog.Info("Server is going to restart.")
  216. me.alive = false
  217. me.exitstatus = STATUS_RESTART
  218. }
  219. func (me * Server) Close() {
  220. monolog.Info("Closing server, shutting down tickers.")
  221. for name, _ := range me.tickers {
  222. me.RemoveTicker(name);
  223. }
  224. monolog.Info("Closing server, shutting down clients.")
  225. for _, client := range me.clients {
  226. if (client.IsAlive()) {
  227. client.Close()
  228. }
  229. }
  230. me.handleDisconnectedClients()
  231. monolog.Info("Closing server, closing logfile.")
  232. me.logfile.Close();
  233. }
  234. func (me * Server) Serve() (status int, err error) {
  235. // Setup random seed here, or whatever
  236. rand.Seed(time.Now().UTC().UnixNano())
  237. go me.handleDisconnectedClients()
  238. for (me.alive) {
  239. if tcplistener, ok := me.listener.(*net.TCPListener) ; ok {
  240. tcplistener.SetDeadline(time.Now().Add(5*time.Second))
  241. }
  242. conn, err := me.listener.Accept()
  243. if err != nil {
  244. if noe, ok := err.(*net.OpError) ; ok && noe.Timeout() {
  245. // it's a timeout. Do nothing, just listen again.
  246. // this to allow the alive flag to do it's work.
  247. } else {
  248. return STATUS_CANNOT_LISTEN, err
  249. }
  250. } else {
  251. go me.onConnect(conn)
  252. }
  253. }
  254. return me.exitstatus, nil
  255. }
  256. func (me * Server) BroadcastString(message string) {
  257. for _, client := range me.clients {
  258. if (client.IsAlive()) {
  259. client.WriteString(message)
  260. }
  261. }
  262. }
  263. func (me * Server) Broadcast(format string, args ...interface{}) {
  264. msg := fmt.Sprintf(format, args...)
  265. me.BroadcastString(msg)
  266. }
  267. // Returns the data path of the server
  268. func (me * Server) DataPath() string {
  269. //
  270. cwd, err := os.Getwd();
  271. if err != nil {
  272. cwd = "."
  273. }
  274. return filepath.Join(cwd, "data", "var")
  275. }
  276. // Returns the script path of the server
  277. func (me * Server) ScriptPath() string {
  278. //
  279. cwd, err := os.Getwd();
  280. if err != nil {
  281. cwd = "."
  282. }
  283. return filepath.Join(cwd, "data", "script")
  284. }