server.go 8.5 KB

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