Receive UDP packets with RMXP
#1
I'm trying to make two (or more) RPG Maker XPs communicate over the network through UDP. The idea is to implement a basic hello/discovery protocol.

I'm using the "module Win32/module Winsock/class Socket/class TCPSocket" library that you find in any netplay-related project.
Plus, I added a UDPSocket class:

Code:
#===============================================================================
# ** UDPSocket - Creates and manages UDP sockets.
#-------------------------------------------------------------------------------
# Author    Ruby
# Version   1.8.1
#===============================================================================

class UDPSocket < 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
    return socket
  end

  #--------------------------------------------------------------------------
  # ? Creates a new socket and connects it to the given host and port.
  #--------------------------------------------------------------------------  
  def initialize
    super(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
  end
end

As you can see it's almost identical to the TCPSocket class, but it uses SOCK_DGRAM and IPPROTO_UDP, and doesn't connect the socket (it's initialized without arguments).

I have managed to send unicast and broadcast to a listener written in ruby that runs outside RPG Maker XP (I have installed Ruby on my Windows machine), but I couldn't make RPG Maker XP receive UDP packets of any kind.

This code sends UDP broadcast packets from RMXP and it works perfectly:

Code:
BROADCAST_ADDR = "255.255.255.255"
ANYCAST_ADDR = "0.0.0.0"
UNICAST = "192.168.0.4"
port = 4323
    
# SEND
    
@socket = UDPSocket.new
@socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_BROADCAST, [0,0,0,1].pack("cccc"), 4)
@socket.connect([Socket::AF_INET, Socket.getservice(port), Socket.getaddress(BROADCAST_ADDR).split(".").collect { |b| b.to_i }].flatten.pack("snC4x8"))
@socket.send("This is a broadcast")

In order to receive broadcast packets, a stand-alone ruby app would do something like this:

Code:
require 'socket'

$port = 4323

sThread = Thread.start do     # run server in a thread
  server = UDPSocket.open
  server.bind('0.0.0.0', $port)
  2000.times { p server.recvfrom(64) }
  server.close
end

sThread.join

# Wait for Enter Key to be pressed.
$stdin.gets

(ignore all the unnecessary code)
which, inside RPG Maker XP, based on the API documentation, would be something like

Code:
# Broadcast
@insocket = UDPSocket.new
@insocket.setsockopt(Socket::SOL_SOCKET, Socket::SO_BROADCAST, [0,0,0,1].pack("cccc"), 4)
@insocket.bind(Socket.sockaddr_in(port, ANYCAST_ADDR))
# Receive data
while true
  if @insocket.ready?
    p @insocket.recvfrom(64)
  end
end

where recvfrom is defined as:

Code:
class Socket
  #--------------------------------------------------------------------------
  # ? Returns received data.
  #--------------------------------------------------------------------------  
  def recvfrom(len, flags = 0)
    buf = "\0" * len
    buf2 = "\0" * 32
    buf3 = "\0" * 4
    SocketError.check if Winsock.recvfrom(@fd, buf, buf.size, flags, buf2, buf3) == -1
    return buf, buf2
  end
end

with also:

Code:
module Winsock
  #--------------------------------------------------------------------------
  # * Receive From
  #--------------------------------------------------------------------------
  def self.recvfrom(*args)
    Win32API.new(DLL, "recvfrom", "ppllpp", "l").call(*args)
  end
end

(ignore all the unnecessary code)
Well, it doesn't work, that is @insocket.ready? returns true, but @insocket.recvfrom returns a string of "\000" values.
The exact behavior is that:
a) when an external app is actually broadcasting data that string is returned at the first received packet and then it is returned immediately for the following calls;
b) when no message is being broadcasted, @insocket.ready? returns true anyway, @insocket.recvfrom does not return at all and of course I get the "the script is hanging" message.

The problem seems to be in the decoding phase, because the difference between a) and b) tells me that RMXP is receiving those packets. Also, @insocket.ready?, which is based on the dll function select, does not work as expected.

So... HEEEEELPPPP!!!!

Update: it appears that this behavior of select with udp socket is a problem for many.
Reply }
#2
Time to bump this.
I managed to receive packets by using recv and not recvfrom, but I can't read the sender's address with recv, and I need that.
select is actually working correctly. recvfrom is to blame.

Update: i found out that recvfrom was returning -1 but the SocketError class was failing to raise an exception, I guess beacuse it didn't recognize it. So, by checking the error number myself, i found out it was a 10014 error, which means some of the buffer lenghts I was passing weren't correct. I turns out the correct call to recvfrom must be:
Code:
#--------------------------------------------------------------------------
  # ? Returns received data.
  #--------------------------------------------------------------------------  
  def recvfrom(len, flags = 0)
    buf = "\0" * len
    buf2 = "\0" * 2
    buf3 = "\0" * 1
    SocketError.check if (result = Winsock.recvfrom(@fd, buf, buf.size, flags, buf2, buf3)) == -1
    return buf, buf2, buf3
  end

Now I can receive the packet. I'm still trying to extract the sender's address.

Update2: (am I talking to myself?)
Wrong, not
Code:
buf2 = "\0" * 2
    buf3 = "\0" * 1
but
Code:
buf2 = "\0" * 20
    buf3 = "\0" * 1

however, it still gives error 10014 sometimes.

Update3: And I managed to get the sender's address too. Be sure to play my online game when it will come out. :) This solo thread can be closed.
Reply }


Possibly Related Threads…
Thread Author Replies Views Last Post
   [RMXP] Showing skill gained by leveling up on battle result FrQise 12 13,022 05-07-2021, 02:05 PM
Last Post: FrQise
   RMXP SKill animations too bright (overlaying the battle screen) Starmage 4 10,326 06-13-2016, 03:41 AM
Last Post: Starmage
   [RMXP]Game Over and Atoa Battle Status mishaps firestalker 8 11,161 08-07-2014, 01:59 AM
Last Post: firestalker
Star Multilanguage script petition for RMXP Iqus 11 19,522 01-07-2013, 10:50 AM
Last Post: Narzew
   [RMXP] Actor Cloning dagarath 2 9,240 03-08-2011, 07:18 AM
Last Post: dagarath
   Replace the AUDIO class for RMXP! DerVVulfman 8 15,508 10-15-2010, 06:29 PM
Last Post: yamina-chan



Users browsing this thread: 2 Guest(s)