CoralMUD-0.15/
CoralMUD-0.15/core/
CoralMUD-0.15/data/
CoralMUD-0.15/data/areas/
CoralMUD-0.15/data/help/
CoralMUD-0.15/data/players/
CoralMUD-0.15/lib/automap/
CoralMUD-0.15/lib/items/
############################################################################
### FRuby Client 1.0 by Retnur
### A big thanks goes out to Kiasyn.
############################################################################
# Copyright (c) 2009, Jeffrey Heath Basurto <bigng22@gmail.com>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
############################################################################

### A list of all known IMC channels.
$imc_channel_list = {:icode=>["Server02:icode", "#R"], 
                     :iruby=>["Server02:iRuby", "#R"],
                     :igame=>["Server02:igame", "#G"], 
                     :ichat=>["Server01:ichat", "#P"],
                     :inews=>["Server02:inews", "#W"],
                     :imudnews=>["Server02:imudnews", "#Y"],
                     :ibuild=>["Server01:ibuild", "#r"],
                     :imusic=>["Server02:imusic", "#p"],
                     :admin=>["Server01:admin", "#w"]}


### String extensions 
###
class String
  def encapsulate
    s = self
    s = s.reverse
    s.concat '"'
    s = s.reverse
    s.concat '"'
    return s
  end
end

### Net::Telnet extensions
###
class Net::Telnet
    def waitfor(options) # :yield: recvdata
      time_out = @options["Timeout"]
      waittime = @options["Waittime"]

      if options.kind_of?(Hash)
        prompt   = if options.has_key?("Match")
                     options["Match"]
                   elsif options.has_key?("Prompt")
                     options["Prompt"]
                   elsif options.has_key?("String")
                     Regexp.new( Regexp.quote(options["String"]) )
                   end
        time_out = options["Timeout"]  if options.has_key?("Timeout")
        waittime = options["Waittime"] if options.has_key?("Waittime")
      else
        prompt = options
      end

      if time_out == false
        time_out = nil
      end

      line = ''
      buf = ''
      rest = ''
      until(not IO::select([@sock], nil, nil, waittime))
        unless IO::select([@sock], nil, nil, time_out)
          raise TimeoutError, "timed out while waiting for more data"
        end
        begin
          c = @sock.readpartial(1024 * 1024)
          @dumplog.log_dump('<', c) if @options.has_key?("Dump_log")
          if @options["Telnetmode"]
            c = rest + c
            if Integer(c.rindex(/#{IAC}#{SE}/no)) <
               Integer(c.rindex(/#{IAC}#{SB}/no))
              buf = preprocess(c[0 ... c.rindex(/#{IAC}#{SB}/no)])
              rest = c[c.rindex(/#{IAC}#{SB}/no) .. -1]
            elsif pt = c.rindex(/#{IAC}[^#{IAC}#{AO}#{AYT}#{DM}#{IP}#{NOP}]?\z/no) ||
                       c.rindex(/\r\z/no)
              buf = preprocess(c[0 ... pt])
              rest = c[pt .. -1]
            else
              buf = preprocess(c)
              rest = ''
            end
         else
           # Not Telnetmode.
           #
           # We cannot use preprocess() on this data, because that
           # method makes some Telnetmode-specific assumptions.
           buf = rest + c
           rest = ''
           unless @options["Binmode"]
             if pt = buf.rindex(/\r\z/no)
               buf = buf[0 ... pt]
               rest = buf[pt .. -1]
             end
             buf.gsub!(/#{EOL}/no, "\n")
           end
          end
          @log.print(buf) if @options.has_key?("Output_log")
          line += buf
          yield buf if block_given?
        rescue EOFError # End of file reached
          if line == ''
            line = nil
            yield nil if block_given?
          end
          break
        end
      end
      line
    end
end
### Look for usage below for examples.
### You should only need to instantiate this a single time.
class IMCclient
  ### This is called when IMCclient is instantiated.
  def initialize(name, pw)
    @connection = Net::Telnet::new( "Host" => "74.207.247.83",
                                    "Port" => 5000,
                                    "Telnetmode" => false,
                                    "Prompt" => /\n/)
    
    # Start authenticating. Will autosetup if IMC server does not have configuration.
    @connection.puts "PW #{name} #{pw} version=2 autosetup #{pw}2"
    # Set the sequence, which is a number associated with each packet
    @sequence = Time.now.to_i
    @myname = name
    @chat_log = []
    @@client_thread = Thread.new do
      loop do
        $imclock.synchronize do
          $imcclient.accept_data
        end
        sleep 0.2
      end
    end
  end
 
  ### call this in some event.  It really should called at least twice a second.
  ### This continually checks for incoming packets
  def accept_data 
    # empty string
    s= ""
    ### read from connection and put it in s
    begin
      @connection.waitfor({"Timeout"=>false, "Waittime"=>0})  do |c|
        break if c == nil
        s << c
      end
    rescue
      #$imcclient = nil
    end
    ### If s is empty we return.
    return if s.chomp.empty? 
    ### Search for new packets line by line and process.
    while ( (line = s.pop_line) != nil )
      ### Does something with each line. Probably a chat message.
      handle_server_input line
    end
  end
 
  ### Handles raw input
  ### Directs towards packets.
  def handle_server_input(s)
    case s.strip
      #On initial client connection:
      #SERVER Sends: autosetup <servername> accept <networkname> (SHA256-SET)
    when /^autosetup (\S+) accept (\S+)$/
      log :debug, "Autosetup complete. Connected to #{$1} on network #{$2}"
    when /^PW Server\d+ domyhomework version=2 (\S+)$/i
      log :debug, "IMC Authentication complete"
      send_isalive
    when /^(\S+) \d+ \S+ (\S+) (\S+)$/i
      handle_packet( $1, $2, $3 )
    when /^(\S+) \d+ \S+ (\S+) (\S+) (.*)$/i
      handle_packet( $1, $2, $3, $4 )
    else
      # meh
    end
    #You@YourMUD 1234567890 YourMUD tell Dude@SomeMUD text="Having fun?"
  end
  ### Processes all packets and directs it.
  def handle_packet( sender, type, target, data=nil )
    if data != nil and data.is_a?( String ) then
      new_data = Hash.new

      data.strip!
      data = StringScanner.new(data) 
      while !data.eos?
        key = data.scan(/\w+/) ### grab a key
        data.skip(/=/) ### skip the =
        if data.peek(1).eql?('"')
          val = data.scan(/"([^"\\]*(\\.[^"\\]*)*)"/)
          val.slice!(0)
          val.slice!(-1)
        else
          val = ""
          while (!data.eos? && !data.peek(1).eql?(' '))
            val << data.getch
          end
        end
        data.skip(/\s+/)
        new_data[key] = val
      end
      data = new_data
    end
  #  return if sender.include?("@#{@myname}")
  #  return if data != nil and data.include? 'sender' and data['sender'].include? "@#{@myname}"
    #You@YourMUD 1234567890 YourMUD tell Dude@SomeMUD text="Having fun?"
    case type
    when "keepalive-request"
      send_isalive
    when "tell"
      $dplayer_list.each do |dplayer|
        if dplayer.name == target.split('@')[0]
          dplayer.text_to_player("#R#{sender} tells you, '" + data['text'] + "#R'#n" + ENDL)
          break
        end
        $imcclient.private_send("FakeCoralMud", *sender.split('@'), "#{target.split('@')[0]} was not found on CoralMUD.")
      end
    when "ice-msg-b"
      cn = data['channel'].split(":")
      cn[1].strip!
      $dplayer_list.each do |dPlayer|      
        if dPlayer.channel_flags[cn[1].to_sym] != nil
          next
        end

        chan_data = $imc_channel_list[cn[1].to_sym]
        if chan_data != nil
          col = chan_data[1]
        else
          col = "#D"
        end
        data['text'].gsub!('#', '##')
        data['text'].gsub!('\"', '"')
        dPlayer.text_to_player "#{col}(#{cn[1]}) #{sender} says, '#{data['text']}'" + ENDL
        log_chat  Time.now.strftime("#{col}[%I:%M%p]"), cn[1], "(#{cn[1]}) #{sender} says, '#{data['text']}'\r\n"
      end
    when "ice-msg-p"
      cn = data['channel'].split(":")
      $dplayer_list.each do |dPlayer|
        if dPlayer.channel_flags[cn[1].to_sym] != nil
          next
        end
        dPlayer.text_to_player "#p(#{cn[1]}) #{data['realfrom']} says, '#{data['text']}'#n" + ENDL
      end
    when "ice-msg-r"
      cn = data['channel'].split(":")
      $dplayer_list.each do |dPlayer|
        if dPlayer.channel_flags[cn[1].to_sym] != nil
          next
        end
        dPlayer.text_to_player "#p(#{cn[1]}) #{data['realfrom']} says, '#{data['text']}'#n" + ENDL
      end
    end
  end

  ### Sends a packet in the right format.
  def packet_send( sender, type, target, destination, data)
    data_out = ""
    if data.is_a?( Hash ) then
      hash = data.to_hash
      hash.each do |k, v|
        v.to_s.gsub!('"', '\"')
        if v.to_s.include? " " then
          v = v.encapsulate
        end
        data_out.concat "#{k}=#{v} "
      end
    end
    packet = "#{sender}@#{@myname} #{@sequence} #{@myname} #{type} #{target}@#{destination} #{data_out}"
    @sequence += 1
    @connection.puts packet
  end

  def private_send(sender, target, destination, message, type='tell')
    packet_send(sender, type, target, destination, {:text=>message})
  end

  ### This formats for a specific channel.
  ### channel_send "Retnur", Server01:ichat, "example string"
  def channel_send( sender, channel, message, type='ice-msg-b')
    packet_send( sender, type, '*', '*', {:channel => channel, :text => message, :emote => 0, :echo => 1 })
  end

  ### Sends info to IMC server about mud.
  ### host is your muds address, port is its port.  url is your web site.
  ### This isn't required.
  def send_isalive
    packet_send( "*", "is-alive", "*", "*", { :versionid => "CoralMud Client1.0", :url => "", :host => "", :port => 0} )
  end

  ### Shut the IMCclient down.
  def shutdown
    log :debug, "IMC Connection closed."
    @connection.close
  end

  # keeps a chatlog of the last 50 events. Not per channel.
  def log_chat time, chan, string
    @chat_log << [time, chan, string]
    #@chat_log.delete @chat_log[-1] if @chat_log.length > 50
  end

  def ret_log chan 
    s = "_#{chan}_Logs_________________________________________________________\r\n"
    @chat_log.each_index do |i|
      if @chat_log[i][1].include? chan
        s << (@chat_log[i][0] + @chat_log[i][2])    
      end
    end
    return s
  end
end




### Beginning of actual execution.
### You ned to change this name and password.
### create our client and a lock for it.

$imcclient = IMCclient.new("FakeCoralMud", "domyhomework")
$imclock = Mutex.new