02-18-2010, 10:38 PM
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:
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:
In order to receive broadcast packets, a stand-alone ruby app would do something like this:
(ignore all the unnecessary code)
which, inside RPG Maker XP, based on the API documentation, would be something like
where recvfrom is defined as:
with also:
(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.
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.