P
Pierre Barbier de Reuille
Hello there !
I was trying to code a scriptable MUD client (following the RubyQuiz
challenge). For the moment, I implemented a scriptable telnet which
works fine with my mail server and also with the test server provided by
James Edward Gray II on the RubyQuiz website. However, when I tried it
on a real MUD server, it just stopped receiving data after a few lines
(I tested it on imperian.com:23). The connection is not lost however, as
I can send commands (simple: if you can send "3", it will exit your
session ).
I join my code here, all the connection stuff are in the two methods
"connect" and "idle" of the MUD class.
Thanks,
Pierre
Code :
======8<============8<===========8<===========8<============8<=========
#!/usr/bin/ruby -w
require "socket"
class MUD
attr_reader :url, ort, refix, :io
attr_reader :commands, :answers
def initialize(url, port)
@url = url
@port = port
@answers = MUDFilters.new
@commands = MUDFilters.new
@prefix = "!"
@io = MUD::Io.new
register_defaults
end
def connect
@io.send ||= lambda { |line| @io.server_socket.write line }
@io.report ||= lambda { |line| $stdout.write line }
@socket = TCPSocket.new(@url, @port)
@io.report["Connected to #{@url}:#{@port}\n"]
@io.server_socket = @socket
@io.freeze
@url.freeze
@prefix.freeze
@thread = Thread.new do
idle
end
end
def disconnect
@io.server_socket.close unless @io.server_socket.closed?
@thread.join
puts "Disconnected ..."
end
def idle
f = File.new("log_server", "wb")
begin
while line = @io.server_socket.gets
f.write line
process_answer line
end
rescue
end
@io.server_socket.close unless @io.server_socket.closed?
return 0
end
def process_command(line)
process(@commands, line)
end
alias_method :send, rocess_command
def process_answer(line)
process(@answers, line)
end
def add_command(regexp=//, priority=0, &block)
register(@commands, Rule.new(regexp, priority, &block))
end
def remove_command(id)
unregister(@commands, id)
end
def add_answer(regexp=//, priority=0, &block)
register(@answers, Rule.new(regexp, priority, &block))
end
def remove_answer(id)
unregister(@answers, id)
end
protected
def register_defaults
# Echo the server commands if nothing else
add_answer do |io,line|
io.report[line]
end
# Send the commands to the server
add_command do |io,line|
io.send[line]
end
# Load a new definition file
add_command(/^#{@prefix}load/i, 10000) do |io,line|
if line =~ /^#{@prefix}load (.*)$/i
config = $1
if config and FileTest.exists? config and FileTest.readable?(config)
io.report["Loading file #{config} ...\n"]
load(config,true)
end
end
nil
end
end
def process(filters,line)
selected = []
filters.each do |regexp, rules|
if line =~ regexp
selected += rules
end
end
selected.sort!
selected.each do |rule|
#puts "Launching rule #{rule.regexp.inspect} with priority
#{rule.priority}"
line = rule.block[@io,line]
if line == nil
return
end
end
end
def register(filters, rule)
filters[rule.regexp] << rule
filters.ids[rule.object_id] = rule.regexp
rule.object_id
end
def unregister(filters, id)
regexp = filters.ids[id]
filters[regexp].delete_if do |rule|
rule.object_id == id
end
end
class Rule
attr_accessor riority, :block
attr_reader :regexp
def initialize(regexp, priority, &block)
@regexp = regexp
@priority = priority
@block = block
end
def <=>(other)
other.priority <=> @priority
end
end
class Io
attr_accessor :send, :report
attr_accessor :server_socket
end
class MUDFilters
attr_accessor :ids
include Enumerable
def initialize
@filters = Hash.new { |h,k| h[k] = [] }
@ids = Hash.new
end
def [](regexp)
@filters[regexp]
end
def []=(regexp, value)
@filters[regexp] = value
end
def each(&block)
@filters.each(&block)
end
end
end
if $0 == __FILE__
require "getoptlong"
opts = GetoptLong.new(
[ "--port", "-p", GetoptLong::OPTIONAL_ARGUMENT ],
[ "--config", "-c", GetoptLong::OPTIONAL_ARGUMENT ],
[ "--prefix", GetoptLong::OPTIONAL_ARGUMENT],
[ "--prompt", GetoptLong::OPTIONAL_ARGUMENT]
)
config = nil
port = 23
prefix = "!"
opts.each do |opt, arg|
case opt
when "--port"
port = arg.to_i
when "--config"
config = arg
when "--prefix"
prefix = Regexp.escape arg
end
end
if ARGV.length != 1
$stderr.write <<-EOF
Usage: #{$0} [--port PORT] [--config FILE] [--prefix PREFIX] [--] HOST
PORT port of the host to connect (default: #{port.to_s})
FILE ruby file imported with a main function called with the
MUD object (default:none)
HOST host to connect
PREFIX prefix for local commands (default:#{prefix})
EOF
exit 2
end
host = ARGV[0]
$mud = MUD.new(host, port)
$mud.prefix.replace(prefix)
if config and FileTest.exists? config and FileTest.readable? config
load(config,true)
end
$mud.connect
begin
loop do
line = $stdin.readline
$mud.send line
end
rescue EOFError
$mud.disconnect
end
end
======>8============>8===========>8===========>8============>8=========
I was trying to code a scriptable MUD client (following the RubyQuiz
challenge). For the moment, I implemented a scriptable telnet which
works fine with my mail server and also with the test server provided by
James Edward Gray II on the RubyQuiz website. However, when I tried it
on a real MUD server, it just stopped receiving data after a few lines
(I tested it on imperian.com:23). The connection is not lost however, as
I can send commands (simple: if you can send "3", it will exit your
session ).
I join my code here, all the connection stuff are in the two methods
"connect" and "idle" of the MUD class.
Thanks,
Pierre
Code :
======8<============8<===========8<===========8<============8<=========
#!/usr/bin/ruby -w
require "socket"
class MUD
attr_reader :url, ort, refix, :io
attr_reader :commands, :answers
def initialize(url, port)
@url = url
@port = port
@answers = MUDFilters.new
@commands = MUDFilters.new
@prefix = "!"
@io = MUD::Io.new
register_defaults
end
def connect
@io.send ||= lambda { |line| @io.server_socket.write line }
@io.report ||= lambda { |line| $stdout.write line }
@socket = TCPSocket.new(@url, @port)
@io.report["Connected to #{@url}:#{@port}\n"]
@io.server_socket = @socket
@io.freeze
@url.freeze
@prefix.freeze
@thread = Thread.new do
idle
end
end
def disconnect
@io.server_socket.close unless @io.server_socket.closed?
@thread.join
puts "Disconnected ..."
end
def idle
f = File.new("log_server", "wb")
begin
while line = @io.server_socket.gets
f.write line
process_answer line
end
rescue
end
@io.server_socket.close unless @io.server_socket.closed?
return 0
end
def process_command(line)
process(@commands, line)
end
alias_method :send, rocess_command
def process_answer(line)
process(@answers, line)
end
def add_command(regexp=//, priority=0, &block)
register(@commands, Rule.new(regexp, priority, &block))
end
def remove_command(id)
unregister(@commands, id)
end
def add_answer(regexp=//, priority=0, &block)
register(@answers, Rule.new(regexp, priority, &block))
end
def remove_answer(id)
unregister(@answers, id)
end
protected
def register_defaults
# Echo the server commands if nothing else
add_answer do |io,line|
io.report[line]
end
# Send the commands to the server
add_command do |io,line|
io.send[line]
end
# Load a new definition file
add_command(/^#{@prefix}load/i, 10000) do |io,line|
if line =~ /^#{@prefix}load (.*)$/i
config = $1
if config and FileTest.exists? config and FileTest.readable?(config)
io.report["Loading file #{config} ...\n"]
load(config,true)
end
end
nil
end
end
def process(filters,line)
selected = []
filters.each do |regexp, rules|
if line =~ regexp
selected += rules
end
end
selected.sort!
selected.each do |rule|
#puts "Launching rule #{rule.regexp.inspect} with priority
#{rule.priority}"
line = rule.block[@io,line]
if line == nil
return
end
end
end
def register(filters, rule)
filters[rule.regexp] << rule
filters.ids[rule.object_id] = rule.regexp
rule.object_id
end
def unregister(filters, id)
regexp = filters.ids[id]
filters[regexp].delete_if do |rule|
rule.object_id == id
end
end
class Rule
attr_accessor riority, :block
attr_reader :regexp
def initialize(regexp, priority, &block)
@regexp = regexp
@priority = priority
@block = block
end
def <=>(other)
other.priority <=> @priority
end
end
class Io
attr_accessor :send, :report
attr_accessor :server_socket
end
class MUDFilters
attr_accessor :ids
include Enumerable
def initialize
@filters = Hash.new { |h,k| h[k] = [] }
@ids = Hash.new
end
def [](regexp)
@filters[regexp]
end
def []=(regexp, value)
@filters[regexp] = value
end
def each(&block)
@filters.each(&block)
end
end
end
if $0 == __FILE__
require "getoptlong"
opts = GetoptLong.new(
[ "--port", "-p", GetoptLong::OPTIONAL_ARGUMENT ],
[ "--config", "-c", GetoptLong::OPTIONAL_ARGUMENT ],
[ "--prefix", GetoptLong::OPTIONAL_ARGUMENT],
[ "--prompt", GetoptLong::OPTIONAL_ARGUMENT]
)
config = nil
port = 23
prefix = "!"
opts.each do |opt, arg|
case opt
when "--port"
port = arg.to_i
when "--config"
config = arg
when "--prefix"
prefix = Regexp.escape arg
end
end
if ARGV.length != 1
$stderr.write <<-EOF
Usage: #{$0} [--port PORT] [--config FILE] [--prefix PREFIX] [--] HOST
PORT port of the host to connect (default: #{port.to_s})
FILE ruby file imported with a main function called with the
MUD object (default:none)
HOST host to connect
PREFIX prefix for local commands (default:#{prefix})
EOF
exit 2
end
host = ARGV[0]
$mud = MUD.new(host, port)
$mud.prefix.replace(prefix)
if config and FileTest.exists? config and FileTest.readable? config
load(config,true)
end
$mud.connect
begin
loop do
line = $stdin.readline
$mud.send line
end
rescue EOFError
$mud.disconnect
end
end
======>8============>8===========>8===========>8============>8=========