RocketMUD-0.3/help/
RocketMUD-0.3/log/
RocketMUD-0.3/players/
#!ruby
#
# RocketMUD was written by Jon Lambert, 2006.
# It is based on SocketMUD(tm) written by Brian Graversen.
# This code is released to the public domain.
#
#
require 'socket'
require 'fcntl'
require 'yaml'
require 'singleton'
require 'pp'

require 'constants'


##############################
# End of standard definitons #
##############################


#############################
# New structures            #
#############################

# the actual structures

class Help
  attr_accessor :load_time, :keyword, :text

  def initialize k, t
    @load_time = Time.now.to_i
    @keyword = k
    @text = t
  end
end

class Command
  attr_accessor :cmd_name, :cmd_funct, :level

  def initialize n, f, l
    @cmd_name = n
    @cmd_funct = f
    @level = l
  end
end

#############################
# End of new structures     #
#############################

####################
# Global Variables #
####################

$dsock_list = []         # the linked list of active sockets
$dmobile_list = []       # the mobile list of active mobiles
$shut_down = false       # used for shutdown
$current_time = Time.now # let's cut down on calls to time()
$fSet = []             # the socket list for polling

$help_list = []        # the linked list of help files
$tabCmd = []           # the command table
$greeting = ""         # the welcome greeting
$motd = ""             # the MOTD help file

# the color table...
$ansi_table = [
  [ ?d,  "30",  false ],
  [ ?D,  "30",  true ],
  [ ?r,  "31",  false ],
  [ ?R,  "31",  true ],
  [ ?g,  "32",  false ],
  [ ?G,  "32",  true ],
  [ ?y,  "33",  false ],
  [ ?Y,  "33",  true ],
  [ ?b,  "34",  false ],
  [ ?B,  "34",  true ],
  [ ?p,  "35",  false ],
  [ ?P,  "35",  true ],
  [ ?c,  "36",  false ],
  [ ?C,  "36",  true ],
  [ ?w,  "37",  false ],
  [ ?W,  "37",  true ]
]


#
# The command table, very simple, but easy to extend.
#
$tabCmd = [
 # command          function        Req. Level
 # ---------------------------------------------
  Command.new("commands",  :cmd_commands, LEVEL_GUEST),
  Command.new("help",      :cmd_help,     LEVEL_GUEST),
  Command.new("linkdead",  :cmd_linkdead, LEVEL_ADMIN),
  Command.new("say",       :cmd_say,      LEVEL_GUEST),
  Command.new("save",      :cmd_save,     LEVEL_GUEST),
  Command.new("shutdown",  :cmd_shutdown, LEVEL_GOD),
  Command.new("quit",      :cmd_quit,     LEVEL_GUEST),
  Command.new("who",       :cmd_who,      LEVEL_GUEST)
]

###########################
# End of Global Variables #
###########################

require 'utils'
#require 'help'
#require 'event'
#require 'sockdesc'
#require 'mobile'

def load_control
  loaded = false
  Dir.entries(".").each do |entry|
   begin
    next if entry =~ /rocket|constants/ || entry !~ /\.rb/
    if last_modified(entry) > $load_time.to_i
      Kernel::load(entry)
      log_string "Loaded #{entry}"
      loaded = true
    end
   rescue
    log_string "Unable to load #{entry}"
    log_string $!.to_s
   end
  end
  if loaded
    $load_time = Time.now
  end
end

def game_loop control
  tv = 0.0
  rFd = []

  # set this for the first loop
  last_time = Time.now

  # clear out the file socket set
  $fSet = []

  # add control to the set
  $fSet << control

  # do this untill the program is shutdown
  while !$shut_down
    # set current_time
    $current_time = Time.now

    # copy the socket set
    rFd = $fSet.dup

    # wait for something to happen
    # Poll our socket interest set
    rFd, dummy, dummy = select(rFd, nil, nil, tv)

    # check for new connections
    if rFd && rFd.include?(control)
      newConnection = control.accept
      if newConnection
        new_socket newConnection
      end
    end

    # poll sockets in the socket list
    $dsock_list.each do |dsock|
      #
      # Close sockects we are unable to read from.

      if rFd && rFd.include?(dsock.control) && !dsock.read_from_socket
        dsock.close_socket false
        next
      end

      # Ok, check for a new command
      dsock.next_cmd_from_buffer

      # Is there a new command pending ?
      if !dsock.next_command.empty?
        # figure out how to deal with the incoming command
        case dsock.state
        when :state_new_name, :state_new_password, :state_verify_password, :state_ask_password
          dsock.handle_new_connections dsock.next_command
        when :state_playing
          dsock.handle_cmd_input dsock.next_command
        else
          bug "Descriptor in bad state."
        end
        dsock.next_command = ''
      end

      # if the player quits or get's disconnected
      next if dsock.state == :state_closed

      # Send all new data to the socket and close it if any errors occour
      if !dsock.flush_output
        dsock.close_socket false
      end
    end

    # call the event queue
    heartbeat

    #
    # Here we sleep out the rest of the pulse, thus forcing
    # RocketMud to run at PULSES_PER_SECOND pulses each second.
    # get the time right now, and calculate how long we should sleep
    sleep_time = last_time - Time.now + (1.0 / PULSES_PER_SECOND)
    # if secs < 0 we don't sleep, since we have encountered a laghole
    if sleep_time > 0
      sleep sleep_time
      next
    end

    load_control

    # reset the last time we where sleeping
    last_time = Time.now

    # recycle sockets
    $dsock_list.each do |dsock|
      next if dsock.state != :state_closed

      # remove the socket from the socket list
      $dsock_list.delete dsock

      # close the socket
      dsock.control.close
    end
  end # while
end

#
# New_socket()
#
# Initializes a new socket, get's the hostname
# and puts it in the active socket_list.
def new_socket sock
  #
  # allocate some memory for a new socket if
  # there is no free socket in the free_list
  sock_new = SockDesc.new sock

  # attach the new connection to the socket list
  $fSet << sock

  # set the socket as non-blocking
  sock.fcntl Fcntl::F_SETFL, Fcntl::O_NONBLOCK unless RUBY_PLATFORM =~ /win32/

  # update the linked list of sockets
  $dsock_list << sock_new

  # send the greeting
  sock_new.text_to_buffer $greeting
  sock_new.text_to_buffer "What is your name? "

  # initialize socket events
  init_events_socket sock_new

  # everything went as it was supposed to
  return true
end

def termguard(sig)
  bug "The server is shutting down, attempting to close MUD."
  buf = sprintf("\r\nThe server is shutting down!\r\n");

  # inform all players and save them
  $dsock_list.each do |dsock|
    dsock.text_to_socket buf
    if dsock.state == :state_playing && dsock.player
      save_player dsock.player
    end
  end

  # close MUD
  exit 1
end

if __FILE__ == $0
  Signal.trap("INT", method(:termguard))
  Signal.trap("TERM", method(:termguard))
  Signal.trap("KILL", method(:termguard))

  load_control
  # note that we are booting up
  log_string "Program starting."

  # initialize the event queue - part 1
  init_event_queue 1

  # initialize the socket
  servsock = TCPServer.new '0.0.0.0', MUDPORT
  servsock.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true) unless RUBY_PLATFORM =~ /cygwin/
  servsock.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, [0,0].pack('ii'))
  servsock.fcntl Fcntl::F_SETFL, Fcntl::O_NONBLOCK unless RUBY_PLATFORM =~ /win32/

  # load all external data
  load_muddata

  # initialize the event queue - part 2
  init_event_queue 2

  # main game loop
  game_loop servsock

  # close down the socket
  servsock.close

  # terminated without errors
  log_string "Program terminated without errors."
  exit 0
end