Envision, Create, Share

Welcome to HBGames, a leading amateur game development forum and Discord server. All are welcome, and amongst our ranks you will find experts in their field from all aspects of video game design and development.

[Ruby] messing around with sockets and irc

I am trying to get a working ruby irc script to work within RPG Maker XP.

I am having some problems.

I have encountered an error that I don't understand:

cannot convert nil into IO

I have highlighted the line in the pastebin dump below (line 534):

Does anybody have any ideas?

[pastebin]82[/pastebin]
 
Sorry to test it you'll need this script above it:

Code:
#===============================================================================

# ** Socket - Creates and manages sockets.

#-------------------------------------------------------------------------------

# Author    Ruby

# Version   1.8.1

#===============================================================================

 

class Socket

 

  #--------------------------------------------------------------------------

  # ● Constants

  #--------------------------------------------------------------------------

  AF_UNSPEC                 = 0  

  AF_UNIX                   = 1

  AF_INET                   = 2

  AF_IPX                    = 6

  AF_APPLETALK              = 16

 

  PF_UNSPEC                 = 0  

  PF_UNIX                   = 1

  PF_INET                   = 2

  PF_IPX                    = 6

  PF_APPLETALK              = 16

 

  SOCK_STREAM               = 1

  SOCK_DGRAM                = 2

  SOCK_RAW                  = 3

  SOCK_RDM                  = 4

  SOCK_SEQPACKET            = 5

 

  IPPROTO_IP                = 0

  IPPROTO_ICMP              = 1

  IPPROTO_IGMP              = 2

  IPPROTO_GGP               = 3

  IPPROTO_TCP               = 6

  IPPROTO_PUP               = 12

  IPPROTO_UDP               = 17

  IPPROTO_IDP               = 22

  IPPROTO_ND                = 77

  IPPROTO_RAW               = 255

  IPPROTO_MAX               = 256

 

  SOL_SOCKET                = 65535

 

  SO_DEBUG                  = 1

  SO_REUSEADDR              = 4

  SO_KEEPALIVE              = 8

  SO_DONTROUTE              = 16

  SO_BROADCAST              = 32

  SO_LINGER                 = 128

  SO_OOBINLINE              = 256

  SO_RCVLOWAT               = 4100

  SO_SNDTIMEO               = 4101

  SO_RCVTIMEO               = 4102

  SO_ERROR                  = 4103

  SO_TYPE                   = 4104

  SO_SNDBUF                 = 4097

  SO_RCVBUF                 = 4098

  SO_SNDLOWAT               = 4099

 

  TCP_NODELAY               =   1

 

  MSG_OOB                   = 1

  MSG_PEEK                  = 2

  MSG_DONTROUTE             = 4

 

  IP_OPTIONS                =   1

  IP_DEFAULT_MULTICAST_LOOP =   1

  IP_DEFAULT_MULTICAST_TTL  =   1

  IP_MULTICAST_IF           =   2

  IP_MULTICAST_TTL          =   3

  IP_MULTICAST_LOOP         =   4

  IP_ADD_MEMBERSHIP         =   5

  IP_DROP_MEMBERSHIP        =   6

  IP_TTL                    =   7

  IP_TOS                    =   8

  IP_MAX_MEMBERSHIPS        =   20

 

  EAI_ADDRFAMILY            = 1

  EAI_AGAIN                 = 2

  EAI_BADFLAGS              = 3

  EAI_FAIL                  = 4

  EAI_FAMILY                = 5

  EAI_MEMORY                = 6

  EAI_NODATA                = 7

  EAI_NONAME                = 8

  EAI_SERVICE               = 9

  EAI_SOCKTYPE              = 10

  EAI_SYSTEM                = 11

  EAI_BADHINTS              = 12

  EAI_PROTOCOL              = 13

  EAI_MAX                   = 14

 

  AI_PASSIVE                = 1

  AI_CANONNAME              = 2

  AI_NUMERICHOST            = 4

  AI_MASK                   = 7

  AI_ALL                    = 256

  AI_V4MAPPED_CFG           = 512

  AI_ADDRCONFIG             = 1024

  AI_DEFAULT                = 1536

  AI_V4MAPPED               = 2048

 

  #--------------------------------------------------------------------------

  # ● Returns the associated IP address for the given hostname.

  #--------------------------------------------------------------------------  

  def self.getaddress(host)

    gethostbyname(host)[3].unpack("C4").join(".")

  end

 

  #--------------------------------------------------------------------------

  # ● Returns the associated IP address for the given hostname.

  #--------------------------------------------------------------------------  

  def self.getservice(serv)

    case serv

    when Numeric

      return serv

    when String

      return getservbyname(serv)

    else

      raise "Please us an interger or string for services."

    end

  end

 

  #--------------------------------------------------------------------------

  # ● Returns information about the given hostname.

  #--------------------------------------------------------------------------

  def self.gethostbyname(name)

    raise SocketError::ENOASSOCHOST if (ptr = Winsock.gethostbyname(name)) == 0

    host = ptr.copymem(16).unpack("iissi")

    [host[0].copymem(64).split("\0")[0], [], host[2], host[4].copymem(4).unpack("l")[0].copymem(4)]

  end

 

  #--------------------------------------------------------------------------

  # ● Returns the user's hostname.

  #--------------------------------------------------------------------------  

  def self.gethostname

    buf = "\0" * 256

    Winsock.gethostname(buf, 256)

    buf.strip

  end

 

  #--------------------------------------------------------------------------

  # ● Returns information about the given service.

  #--------------------------------------------------------------------------

  def self.getservbyname(name)

    case name

    when /echo/i

      return 7

    when /daytime/i

      return 13

    when /ftp/i

      return 21

    when /telnet/i

      return 23

    when /smtp/i

      return 25

    when /time/i

      return 37

    when /http/i

      return 80

    when /pop/i

      return 110

    else

      raise "Service not recognized."

    end

  end

 

  #--------------------------------------------------------------------------

  # ● Creates an INET-sockaddr struct.

  #--------------------------------------------------------------------------  

  def self.sockaddr_in(port, host)

    begin

      [AF_INET, getservice(port)].pack("sn") + gethostbyname(host)[3] + [].pack("x8")

    rescue

      nil

    end

  end

 

  #--------------------------------------------------------------------------

  # ● Creates a new socket and connects it to the given host and port.

  #--------------------------------------------------------------------------  

  def self.open(*args)

    socket = new(*args)

    if block_given?

      begin

        yield socket

      ensure

        socket.close

      end

    end

    nil

  end

 

  #--------------------------------------------------------------------------

  # ● Creates a new socket.

  #--------------------------------------------------------------------------  

  def initialize(domain, type, protocol)

    SocketError.check if (@fd = Winsock.socket(domain, type, protocol)) == -1

    @fd

  end

 

  #--------------------------------------------------------------------------

  # ● Accepts incoming connections.

  #--------------------------------------------------------------------------  

  def accept(flags = 0)

    buf = "\0" * 16

    SocketError.check if Winsock.accept(@fd, buf, flags) == -1

    buf

  end

 

  #--------------------------------------------------------------------------

  # ● Binds a socket to the given sockaddr.

  #--------------------------------------------------------------------------  

  def self.bind(sockaddr)

    SocketError.check if (ret = Winsock.bind(@fd, sockaddr, sockaddr.size)) == -1

    ret

  end

 

  #--------------------------------------------------------------------------

  # ● Closes a socket.

  #--------------------------------------------------------------------------  

  def close

    SocketError.check if (ret = Winsock.closesocket(@fd)) == -1

    ret

  end

 

  #--------------------------------------------------------------------------

  # ● Connects a socket to the given sockaddr.

  #--------------------------------------------------------------------------  

  def connect(sockaddr)

    ret = Winsock.connect(@fd, sockaddr, sockaddr.size)

    SocketError.check if ret == -1

    ret

  end

 

  #--------------------------------------------------------------------------

  # ● Listens for incoming connections.

  #--------------------------------------------------------------------------  

  def listen(backlog)

    SocketError.check if (ret = Winsock.listen(@fd, backlog)) == -1

    ret

  end

 

  #--------------------------------------------------------------------------

  # ● Checks waiting data's status.

  #--------------------------------------------------------------------------  

  def select(timeout)

    SocketError.check if (ret = Winsock.select(1, [1, @fd].pack("ll"), 0, 0, [timeout, timeout * 1000000].pack("ll"))) == -1

    ret

  end

 

  #--------------------------------------------------------------------------

  # ● Checks if data is waiting.

  #--------------------------------------------------------------------------  

  def ready?

    not select(0) == 0

  end  

 

  #--------------------------------------------------------------------------

  # ● Reads data from socket.

  #--------------------------------------------------------------------------  

  def read(len)

    buf = "\0" * len

    Win32API.new("msvcrt", "_read", "lpl", "l").call(@fd, buf, len)

    buf

  end

 

  #--------------------------------------------------------------------------

  # ● Returns recieved data.

  #--------------------------------------------------------------------------  

  def recv(len, flags = 0)

    buf = "\0" * len

    SocketError.check if Winsock.recv(@fd, buf, buf.size, flags) == -1

    buf

  end

 

  #--------------------------------------------------------------------------

  # ● Sends data to a host.

  #--------------------------------------------------------------------------  

  def send(data, flags = 0)

    SocketError.check if (ret = Winsock.send(@fd, data, data.size, flags)) == -1

    ret

  end

 

  #--------------------------------------------------------------------------

  # * Gets

  #--------------------------------------------------------------------------

  def gets

    # Create buffer

    buffer = ""

    # Loop Until "end of line"

    while (char = recv(1)) != "\n"

      buffer += char

    end

    # Return recieved data

    return buffer

  end

 

  #--------------------------------------------------------------------------

  # ● Writes data to socket.

  #--------------------------------------------------------------------------  

  def write(data)

    Win32API.new("msvcrt", "_write", "lpl", "l").call(@fd, data, 1)

  end

 

end

 

 

 

 

#===============================================================================

# ** TCPSocket - Creates and manages TCP sockets.

#-------------------------------------------------------------------------------

# Author    Ruby

# Version   1.8.1

#===============================================================================

 

class TCPSocket < Socket

 

  #--------------------------------------------------------------------------

  # ● Creates a new socket and connects it to the given host and port.

  #--------------------------------------------------------------------------  

  def self.open(*args)

    socket = new(*args)

    if block_given?

      begin

        yield socket

      ensure

        socket.close

      end

    end

    nil

  end

 

  #--------------------------------------------------------------------------

  # ● Creates a new socket and connects it to the given host and port.

  #--------------------------------------------------------------------------  

  def initialize(host, port)

    super(AF_INET, SOCK_STREAM, IPPROTO_TCP)

    connect([AF_INET, Socket.getservice(port), Socket.getaddress(host).split(".").collect { |b| b.to_i }].flatten.pack("snC4x8"))

  end

 

end

 

#==============================================================================

# ■ SocketError

#------------------------------------------------------------------------------

#  Default exception class for sockets.

#==============================================================================

class SocketError < StandardError

  ENOASSOCHOST = "getaddrinfo: no address associated with hostname."

  def self.check

    errno = Winsock.WSAGetLastError

    errno = Errno.constants.detect { |c| Errno.const_get(c).new.errno == errno }

    if errno != nil

      raise Errno.const_get(errno)

    end

  end

end

 

 

 

<span style="color:#000080; font-style:italic;">=begin

 

<span style="color:#000080; font-style:italic;">= monitor.rb

 

<span style="color:#000080; font-style:italic;">Copyright (C) 2001  Shugo Maeda <shugo@ruby-lang.org>

 

<span style="color:#000080; font-style:italic;">This library is distributed under the terms of the Ruby license.

<span style="color:#000080; font-style:italic;">You can freely distribute/modify this library.

 

<span style="color:#000080; font-style:italic;">== example

 

<span style="color:#000080; font-style:italic;">This is a simple example.

 

<span style="color:#000080; font-style:italic;">  require 'monitor.rb'

<span style="color:#000080; font-style:italic;">  

<span style="color:#000080; font-style:italic;">  buf = []

<span style="color:#000080; font-style:italic;">  buf.extend(MonitorMixin)

<span style="color:#000080; font-style:italic;">  empty_cond = buf.new_cond

<span style="color:#000080; font-style:italic;">  

<span style="color:#000080; font-style:italic;">  # consumer

<span style="color:#000080; font-style:italic;">  Thread.start do

<span style="color:#000080; font-style:italic;">    loop do

<span style="color:#000080; font-style:italic;">      buf.synchronize do

<span style="color:#000080; font-style:italic;">        empty_cond.wait_while { buf.empty? }

<span style="color:#000080; font-style:italic;">        print buf.shift

<span style="color:#000080; font-style:italic;">      end

<span style="color:#000080; font-style:italic;">    end

<span style="color:#000080; font-style:italic;">  end

<span style="color:#000080; font-style:italic;">  

<span style="color:#000080; font-style:italic;">  # producer

<span style="color:#000080; font-style:italic;">  while line = ARGF.gets

<span style="color:#000080; font-style:italic;">    buf.synchronize do

<span style="color:#000080; font-style:italic;">      buf.push(line)

<span style="color:#000080; font-style:italic;">      empty_cond.signal

<span style="color:#000080; font-style:italic;">    end

<span style="color:#000080; font-style:italic;">  end

 

<span style="color:#000080; font-style:italic;">The consumer thread waits for the producer thread to push a line

<span style="color:#000080; font-style:italic;">to buf while buf.empty?, and the producer thread (main thread)

<span style="color:#000080; font-style:italic;">reads a line from ARGF and push it to buf, then call

<span style="color:#000080; font-style:italic;">empty_cond.signal.

 

<span style="color:#000080; font-style:italic;">=end

  

 

#

# Adds monitor functionality to an arbitrary object by mixing the module with

# +include+.  For example:

#

#    require 'monitor.rb'

#    

#    buf = []

#    buf.extend(MonitorMixin)

#    empty_cond = buf.new_cond

#    

#    # consumer

#    Thread.start do

#      loop do

#        buf.synchronize do

#          empty_cond.wait_while { buf.empty? }

#          print buf.shift

#        end

#      end

#    end

#    

#    # producer

#    while line = ARGF.gets

#      buf.synchronize do

#        buf.push(line)

#        empty_cond.signal

#      end

#    end

# 

# The consumer thread waits for the producer thread to push a line

# to buf while buf.empty?, and the producer thread (main thread)

# reads a line from ARGF and push it to buf, then call

# empty_cond.signal.

#

module MonitorMixin

  #

  # FIXME: This isn't documented in Nutshell.

  #

  # Since MonitorMixin.new_cond returns a ConditionVariable, and the example

  # above calls while_wait and signal, this class should be documented.

  #

  class ConditionVariable

    class Timeout < Exception; end

    

    # Create a new timer with the argument timeout, and add the

    # current thread to the list of waiters.  Then the thread is

    # stopped.  It will be resumed when a corresponding #signal 

    # occurs.

    def wait(timeout = nil)

      @monitor.instance_eval {mon_check_owner()}

      timer = create_timer(timeout)

      

      Thread.critical = true

      count = @monitor.instance_eval {mon_exit_for_cond()}

      @waiters.push(Thread.current)

 

      begin

    Thread.stop

        return true

      rescue Timeout

        return false

      ensure

    Thread.critical = true

    begin

      if timer && timer.alive?

        Thread.kill(timer)

      end

      if @waiters.include?(Thread.current)  # interrupted?

        @waiters.delete(Thread.current)

      end

      @monitor.instance_eval {mon_enter_for_cond(count)}

    ensure

      Thread.critical = false

    end

      end

    end

    

 

    # call #wait while the supplied block returns +true+.

    def wait_while

      while yield

    wait

      end

    end

    

    # call #wait until the supplied block returns +true+.

    def wait_until

      until yield

    wait

      end

    end

    

    # Wake up and run the next waiter

    def signal

      @monitor.instance_eval {mon_check_owner()}

      Thread.critical = true

      t = @waiters.shift

      t.wakeup if t

      Thread.critical = false

      Thread.pass

    end

    

    # Wake up all the waiters.

    def broadcast

      @monitor.instance_eval {mon_check_owner()}

      Thread.critical = true

      for t in @waiters

    t.wakeup

      end

      @waiters.clear

      Thread.critical = false

      Thread.pass

    end

    

    def count_waiters

      return @waiters.length

    end

    

    private

 

    def initialize(monitor)

      @monitor = monitor

      @waiters = []

    end

 

    def create_timer(timeout)

      if timeout

    waiter = Thread.current

    return Thread.start {

      Thread.pass

      sleep(timeout)

      Thread.critical = true

      waiter.raise(Timeout.new)

    }

      else

        return nil

      end

    end

  end

  

  def self.extend_object(obj)

    super(obj)

    obj.instance_eval {mon_initialize()}

  end

  

  #

  # Attempts to enter exclusive section.  Returns +false+ if lock fails.

  #

  def mon_try_enter

    result = false

    Thread.critical = true

    if @mon_owner.nil?

      @mon_owner = Thread.current

    end

    if @mon_owner == Thread.current

      @mon_count += 1

      result = true

    end

    Thread.critical = false

    return result

  end

  # For backward compatibility

  alias try_mon_enter mon_try_enter

 

  #

  # Enters exclusive section.

  #

  def mon_enter

    Thread.critical = true

    mon_acquire(@mon_entering_queue)

    @mon_count += 1

  ensure

    Thread.critical = false

  end

  

  #

  # Leaves exclusive section.

  #

  def mon_exit

    mon_check_owner

    Thread.critical = true

    @mon_count -= 1

    if @mon_count == 0

      mon_release

    end

    Thread.critical = false

    Thread.pass

  end

 

  #

  # Enters exclusive section and executes the block.  Leaves the exclusive

  # section automatically when the block exits.  See example under

  # +MonitorMixin+.

  #

  def mon_synchronize

    mon_enter

    begin

      yield

    ensure

      mon_exit

    end

  end

  alias synchronize mon_synchronize

  

  #

  # FIXME: This isn't documented in Nutshell.

  # 

  # Create a new condition variable for this monitor.

  # This facilitates control of the monitor with #signal and #wait.

  #

  def new_cond

    return ConditionVariable.new(self)

  end

 

  private

 

  def initialize(*args)

    super

    mon_initialize

  end

 

  # called by initialize method to set defaults for instance variables.

  def mon_initialize

    @mon_owner = nil

    @mon_count = 0

    @mon_entering_queue = []

    @mon_waiting_queue = []

  end

 

  # Throw a ThreadError exception if the current thread

  # does't own the monitor

  def mon_check_owner

    if @mon_owner != Thread.current

      raise ThreadError, "current thread not owner"

    end

  end

 

  def mon_acquire(queue)

    while @mon_owner && @mon_owner != Thread.current

      queue.push(Thread.current)

      Thread.stop

      Thread.critical = true

    end

    @mon_owner = Thread.current

  end

 

  def mon_release

    @mon_owner = nil

    t = @mon_waiting_queue.shift

    t = @mon_entering_queue.shift unless t

    t.wakeup if t

  end

 

  def mon_enter_for_cond(count)

    mon_acquire(@mon_waiting_queue)

    @mon_count = count

  end

 

  def mon_exit_for_cond

    count = @mon_count

    @mon_count = 0

    return count

  ensure

    mon_release

  end

end

 

# Monitors provide means of mutual exclusion for Thread programming.

# A critical region is created by means of the synchronize method,

# which takes a block.

# The condition variables (created with #new_cond) may be used 

# to control the execution of a monitor with #signal and #wait.

#

# the Monitor class wraps MonitorMixin, and provides aliases

#  alias try_enter try_mon_enter

#  alias enter mon_enter

#  alias exit mon_exit

# to access its methods more concisely.

class Monitor

  include MonitorMixin

  alias try_enter try_mon_enter

  alias enter mon_enter

  alias exit mon_exit

end

 

 

# Documentation comments:

#  - All documentation comes from Nutshell.

#  - MonitorMixin.new_cond appears in the example, but is not documented in

#    Nutshell.

#  - All the internals (internal modules Accessible and Initializable, class

#    ConditionVariable) appear in RDoc.  It might be good to hide them, by

#    making them private, or marking them :nodoc:, etc.

#  - The entire example from the RD section at the top is replicated in the RDoc

#    comment for MonitorMixin.  Does the RD section need to remain?

#  - RDoc doesn't recognise aliases, so we have mon_synchronize documented, but

#    not synchronize.

#  - mon_owner is in Nutshell, but appears as an accessor in a separate module

#    here, so is hard/impossible to RDoc.  Some other useful accessors

#    (mon_count and some queue stuff) are also in this module, and don't appear

#    directly in the RDoc output.

#  - in short, it may be worth changing the code layout in this file to make the

#    documentation easier

 

# Local variables:

# mode: Ruby

# tab-width: 8

# End:

 
 
I suggest removing two variables to tell if recording is happening or not. This will get rid of any potential synchronization errors between the two. I'd just check if @recording.is_a?(IO) and go on from there, using nil for non-recording instances.
 
I found out the reason is that there are methods missing from the socket scripts that the makers didn't bother making because they didn't need them. Which is good for them but not for me :4


Is there a reason we can't require ruby parts other than $LOAD_PATH being set up wrong?

I tried copying the ruby folder to my game folder and requiring the socket file that way but I end up with a module missing error. I can require ruby files from the same folder (I made test.rb (print "test")).
 
As far as I know... socket.so should be located in \Ruby\lib\ruby\1.9.1\i386-mingw32 ( I know, i know, i'm using 1.9 version but i'm pretty sure it's the same for 1.8). Try adding that?

And one more thing... the error on 534 - ready = select([@irc],nil, nil,1)

You're using select function which (I guess) is from Socket? Then maybe you need to add this to upper class IRCClient?

class IRCClient < Socket
 
well it's a .so, and rm doesn't support Ruby-C extensions ... ( like a dll but directly usable in ruby without the use of win32api )
 

Thank you for viewing

HBGames is a leading amateur video game development forum and Discord server open to all ability levels. Feel free to have a nosey around!

Discord

Join our growing and active Discord server to discuss all aspects of game making in a relaxed environment. Join Us

Content

  • Our Games
  • Games in Development
  • Emoji by Twemoji.
    Top