Jump to content


Photo

Faucet Networking


  • Please log in to reply
651 replies to this topic

#101 Bytewin

Bytewin

    GMC Member

  • New Member
  • 92 posts
  • Version:GM8

Posted 30 December 2011 - 07:10 PM

Simple enough for a noob like me to understand.
But very powerful..
Nice! Keep on coding! :medieval:
  • 0
A soldier will fight long and hard for a bit of coloured ribbon. - Napoleon Boneparte

Real programmers don't document. If it was hard to write, it should be hard to understand.

Posted Image

#102 MrRatermat

MrRatermat

    GMC Member

  • GMC Member
  • 15 posts
  • Version:GM8.1

Posted 02 January 2012 - 12:58 PM

I just joined this forum to say, THANK YOU! This has made things much easier!

Without this, i wouldn't bother even making multiplayer games anyway!
You deserve a +1!
  • 0

#103 jon sploder

jon sploder

    GMC Member

  • GMC Member
  • 919 posts

Posted 05 February 2012 - 08:17 AM

So Medo, are you going to add mac address functionality or not? I'd really not prefer having to have 39dll for just that and I'm sure many others use that function feel similarly.
  • 0

#104 Medo42

Medo42

    GMC Member

  • GMC Member
  • 317 posts

Posted 05 February 2012 - 03:57 PM

So Medo, are you going to add mac address functionality or not? I'd really not prefer having to have 39dll for just that and I'm sure many others use that function feel similarly.

This somehow dropped from my todo list before I made a new release. I'll fix that soon (probably next week). MAC address detection will be included in some form.

Edit: New version will include bit manipulation functions (bit_get, bit_set and build_ubyte), minimalistic file reading/writing support (read/write an entire file to/from a buffer) and support for querying MAC addresses. The file functions are there because I needed them for Gang Garrison 2 and keeping them in a different branch would be a pain currently because .ged files can't be merged properly. Besides, they might be useful for you as well, and waiting until I have time to start up the Shared Buffers project again isn't a good option :)

The MAC address function is the last open point now.

Edited by Medo42, 05 February 2012 - 09:36 PM.

  • 1

#105 Ronchon le Nain

Ronchon le Nain

    GMC Member

  • GMC Member
  • 143 posts
  • Version:GM8

Posted 07 February 2012 - 09:37 PM

Greetings.

While studying different alternatives of 39dll, i found this one too : http dll 2
In this one , the autor refers to an issue of 39dll :

The DLL also fixes an annoying bug/feature in 39dll that can cause data to be lost. With 39dll, the maximum amount of data that can be recieved as a whole is limited by the operating system. Windows will only buffer a fixed amount of data, e.g. 64KB. This might not be enough if you're trying to send large files. If too much data is buffered by the receiver, the sender has to wait to send more data. Since 39dll's sendmessage function doesn't wait, part of the data is lost if too much data is sent at once. This DLL does additional buffering to avoid this problem, so no data is ever lost. You can buffer as much data as you want on both the sending side and the receiving side.


Are you aware of this issue , and is it solved in Faucet Networking ?
Thanks!
  • 0
www.ymir-online.eu
Posted Image

#106 Medo42

Medo42

    GMC Member

  • GMC Member
  • 317 posts

Posted 07 February 2012 - 10:19 PM

Greetings.

While studying different alternatives of 39dll, i found this one too : http dll 2
In this one , the autor refers to an issue of 39dll :


The DLL also fixes an annoying bug/feature in 39dll that can cause data to be lost. With 39dll, the maximum amount of data that can be recieved as a whole is limited by the operating system. Windows will only buffer a fixed amount of data, e.g. 64KB. This might not be enough if you're trying to send large files. If too much data is buffered by the receiver, the sender has to wait to send more data. Since 39dll's sendmessage function doesn't wait, part of the data is lost if too much data is sent at once. This DLL does additional buffering to avoid this problem, so no data is ever lost. You can buffer as much data as you want on both the sending side and the receiving side.


Are you aware of this issue , and is it solved in Faucet Networking ?
Thanks!

Yes, this is definitely something Faucet Networking solves, it was in fact one of the main problems I had with 39dll which drove me to start this project. If you send a large block of data at once, it will all be buffered and then fed to the operating system by a background thread. For a longer elaboration you can read my 39dll rant in the first post of this thread, and a comparison of the "correct" way to perform a nonblocking send in 39dll and in this extension. It is possible with 39dll, it's just very convoluted :-)
  • 0

#107 Ronchon le Nain

Ronchon le Nain

    GMC Member

  • GMC Member
  • 143 posts
  • Version:GM8

Posted 08 February 2012 - 09:34 AM

Thanks for the quick answer!
I will definitly consider changing to Faucet then :) Hope it won't be too complicated.
Keep up the good work !
  • 0
www.ymir-online.eu
Posted Image

#108 orange451

orange451

    GMC Member

  • GMC Member
  • 1154 posts
  • Version:GM8.1

Posted 08 February 2012 - 12:26 PM

I was about to be "herpderpderp Gang Garrison used Faucet, you should take their name!". Then I realised you wrote Gang Garrison...

I'm surprised you're still working on the game :3 It's been years since I've played it last.

Very good library though, I can't wait to test it more in depth 8D


[EDIT]
Is there a dll form of this available? I like having these types of things external from my exe's :3

Edited by orange451, 08 February 2012 - 02:17 PM.

  • 0

bC14QqN.pngNrTFeil.png


#109 Medo42

Medo42

    GMC Member

  • GMC Member
  • 317 posts

Posted 08 February 2012 - 08:17 PM

Is there a dll form of this available? I like having these types of things external from my exe's :3

I can include the raw dll in the upcoming update, but I won't include a source file with GML helper scripts at this time. It is worth considering that the use of GML scripts to wrap DLL calls has a considerable overhead.

As for the MAC functionality, I have done some tesing and currently have a function mac_addrs() which returns a comma-separated string with all interface addresses, or an empty string on error (or if no interfaces were found). It automatically excludes loopbacks, tunnel devices and PPP modems.

However, there may still be some interface which does not have a unique physical address, and there is no guarantee either that the addresses are always six bytes long. The function can differentiate between the interface types, but I don't feel a pressing urge to research the physical address format of over 100 different interface types.

Another itchy point is that the function is not very nice to use because Game Maker does not have a string_split function, so to get all the individual MAC addresses you have to iterate over the string like this:
var addrString, pos, mac;
addrString = mac_addrs();
pos = string_pos(",",addrString);
while(pos>0) {
    mac = string_copy(addrString, 1, pos-1);
    addrString = string_delete(addrString, 1, pos);
    show_message(mac);
    pos = string_pos(",",addrString);
}
show_message(addrString);
Since 39dll just blindly takes the first interface and assumes its hardware address to be 6 bytes long, this should give you the same output as 39dll's getmacaddress:
string_copy(mac_addrs(), 1, 17);

Any thoughts?
  • 0

#110 orange451

orange451

    GMC Member

  • GMC Member
  • 1154 posts
  • Version:GM8.1

Posted 08 February 2012 - 11:06 PM

Do you have a Faucet Network logo? I can put like "Powered by Faucet Networking" or something :3
  • 0

bC14QqN.pngNrTFeil.png


#111 Medo42

Medo42

    GMC Member

  • GMC Member
  • 317 posts

Posted 08 February 2012 - 11:24 PM

Do you have a Faucet Network logo? I can put like "Powered by Faucet Networking" or something :3

Sean A did a logo for Rebound, but I don't have an "official" one. If you want to create one that would be awesome.
  • 0

#112 orange451

orange451

    GMC Member

  • GMC Member
  • 1154 posts
  • Version:GM8.1

Posted 09 February 2012 - 12:52 PM

Another question. Does this create it's own thread to handle downloading/uploading the packets? If so, would this be a "speed" boost of sorts compared to 39dll which is limited to the room speed?
  • 0

bC14QqN.pngNrTFeil.png


#113 Medo42

Medo42

    GMC Member

  • GMC Member
  • 317 posts

Posted 09 February 2012 - 02:27 PM

Another question. Does this create it's own thread to handle downloading/uploading the packets? If so, would this be a "speed" boost of sorts compared to 39dll which is limited to the room speed?

Yes, this happens in a separate worker thread. In a normal network game situation this should not affect the transfer speed because you will typically send less than a kilobyte over a single socket per step. However, if you want to transfer a large ammount of data with a single nonblocking call per step, I would imagine 39dll to be slower, because the ammount you can send/receive each step is limited by the socket buffer size in the operating system. I did not test this though.

The (imo) most important reason to have the background thread is that it simplifies the interface. Without that (as it was in an early version of Faucet Net), operations that need multiple steps would have to be done in multiple calls to the library. For example, if you call tcp_connect("ganggarrison.com", 80), the library needs to make a DNS query to find the IPs for ganggarrison.com and then attempt to connect. Since the DNS query can take an unpredictable ammount of time and we don't want blocking calls, tcp_connect can only initiate the process and then return. Without a background thread, the library can't start the actual connection attempt until the game hands control back to the library by calling some function, so you need to call into the library at least twice until the connection is actually established. This is usually not a problem because you will be calling networking functions quite regularly anyway, but it makes the behavior of the library needlessly complicated to describe and understand. With a background worker thread, things you initiate will simply be done, no matter how many steps they take and how often you call into the library.

Edited by Medo42, 09 February 2012 - 02:28 PM.

  • 0

#114 orange451

orange451

    GMC Member

  • GMC Member
  • 1154 posts
  • Version:GM8.1

Posted 09 February 2012 - 05:09 PM

Okay :3


And here's my poor attempt at a "faucet" icon :P

Posted Image
  • 0

bC14QqN.pngNrTFeil.png


#115 Medo42

Medo42

    GMC Member

  • GMC Member
  • 317 posts

Posted 09 February 2012 - 10:11 PM

Faucet Networking v1.3 is here :)

- Added new function mac_addrs() to find the physical addresses of installed networking adapters
- Added minimalistic file reading/writing support (append_file_to_buffer(), write_buffer_to_file())
- Added bit manipulation functions (bit_get(), bit_set(), build_ubyte())

Download here

You can also get the raw DLL (not sure how much use this is to anyone though)

Edited by Medo42, 09 February 2012 - 10:14 PM.

  • 1

#116 wnsrn3436

wnsrn3436

    GMC Member

  • GMC Member
  • 189 posts
  • Version:GM:Studio

Posted 10 February 2012 - 01:55 AM

The new version is very nice.
With the addition of file-related functions, the functions of the buffer has been expanded.
Now, people will stay away from 39dll.
This is certainly feature-rich than 39dll, fast and stable.
  • 0

#117 orange451

orange451

    GMC Member

  • GMC Member
  • 1154 posts
  • Version:GM8.1

Posted 10 February 2012 - 02:16 AM

Can you provide an example on (if possible) how to write and receive bits? Some of my values never exceed 7 :P
  • 0

bC14QqN.pngNrTFeil.png


#118 Medo42

Medo42

    GMC Member

  • GMC Member
  • 317 posts

Posted 10 February 2012 - 02:21 AM

The bit manipulation functions are documented with examples in the help.pdf. There are no functions for reading or writing individual bits from/to buffers and sockets, those only deal with bytes.
  • 0

#119 OldSkoolGamer

OldSkoolGamer

    GMC Member

  • GMC Member
  • 128 posts
  • Version:GM:Studio

Posted 12 February 2012 - 11:22 PM

Well, here's another take on the above icon for you:

Posted Image
  • 0

#120 Medo42

Medo42

    GMC Member

  • GMC Member
  • 317 posts

Posted 13 February 2012 - 12:00 AM

Well, here's another take on the above icon for you:

Posted Image

Cool, thanks to you and to orange. Yours actually has some connection to networking, but I think it's been a while since Ethernet connections actually looked like that :P
  • 0

#121 jon sploder

jon sploder

    GMC Member

  • GMC Member
  • 919 posts

Posted 13 February 2012 - 06:17 AM

Excellent, very excited for the new version. Thanks a lot Medo!

EDIT 2: The problem to the below might be due to this: For some reason my client can only send a message in it's create event: right after I use tcp_connect to get a tcpsocket handle. No other messages can be sent outside this event, unless immediately beforehand I reset the tcpsocket handle with another tcp_connect... Any ideas what I'm doing wrong or how to fix it? I'll post the code if you want it but there is a fair bit of junk in it to fill in reminders for myself for later work.

Edit: I have the scripts:
/*
write_string_delim(socket,string);
argument0: socket
argument1: string will be given a delimiter automatically
*/
write_ushort(argument0,string_length(argument1));
write_string(argument0,argument1);
and
/*
read_string_delim(socekt);
argument0: socket
*/
var length, string;
length = read_ushort(argument0);
string = read_string(argument0,length);
return string;

I'm not sure what's going on because the code:
write_string_delim(argument4,mac_addrs());
will not allow the socket to send it's data, but this returns no errors via the extension precaution function... I'm unsure whether it is the mac address function somehow screwing it up or my code, although it must be noted that my write_string_delim works for every other string I've tested. show_message(get_macadr) returns a proper string mac address so I'm stumped.

Edited by jon sploder, 13 February 2012 - 07:58 AM.

  • 0

#122 Medo42

Medo42

    GMC Member

  • GMC Member
  • 317 posts

Posted 13 February 2012 - 10:03 PM

Without further info there is not much I can say about this. It seems unlikely that the mac_addrs() function would cause sockets to misbehave, because the code for mac_addrs() has no interconnection at all to any other code in the library except for being linked into the same dll.

As for what does go wrong, you could use Wireshark to figure out what actually happens between client and server. That should give you a good starting point for narrowing down the problem in the code.
  • 0

#123 jon sploder

jon sploder

    GMC Member

  • GMC Member
  • 919 posts

Posted 14 February 2012 - 08:30 AM

I found the problem; I was accepting sockets per step in the server, so if I made it only accept once the connection worked. I remember I did this for 39dll and it worked fine (was required). Where am I meant to put the acceptor handle code? (It's not every step)
Current snippet from the code that didn't work:

tcpsock = socket_accept(acceptor);
        datagram = tcp_receive_available(tcpsock);
        if (datagram)
        {
            //check to see if anyone is connecting from the 'wrong' port
            if (socket_local_port(tcpsock) != global.tcpport)
            {
                socket_destroy_abortive(tcpsock); //ungracefully close the socket;                
                chat_add("___someone connected from the wrong port."+string(socket_remote_port(tcpsock)));
            }
            var msgid;
            msgid = read_ubyte(tcpsock);        
            script_execute(global.tcpscript[msgid]);
        }
Changed to :
if (!tcpsock) {tcpsock = socket_accept(acceptor);}
To work.

Edited by jon sploder, 14 February 2012 - 08:31 AM.

  • 0

#124 Medo42

Medo42

    GMC Member

  • GMC Member
  • 317 posts

Posted 14 February 2012 - 06:41 PM

There are several things wrong with this code:
- You attempt to accept a new socket every step and overwrite your old socket with that. I have no idea what you are trying to do there. Typically, a TCP server uses one socket for every connected client, so if you want to support multiple clients, you need to keep track of the socket for each one seperately.
- The code assumes that tcp_receive_avaliable will give you a complete message of some sort - but that's not guaranteed. It will just give you *some* number of bytes. You should not make assumptions about how and where TCP packets will be split up or combined, otherwise your protocol will break under load and in real use, even if you get your data exactly in the neat packets you sent if you test on a LAN.
- The check whether someone is connecting on/from (seems to be inconsistent) the wrong port is useless. If it is supposed to check whether the local port is correct, that can never fail because sockets can't accept connection attempts to a different port. If it's supposed to check if the remote port is correct, you'll have a hard time using this server with Faucet Networking because it has no way to specify a source port with tcp_connect. It's just something that is almost never needed (in fact I can't think of a good reason why a protocol would need it).
  • 0

#125 jon sploder

jon sploder

    GMC Member

  • GMC Member
  • 919 posts

Posted 15 February 2012 - 07:14 AM

Surely I must always look out for new connections, and the way to do it is constantly accept new sockets? My server has one object to do this (handle login and registration) and makes a new object for each new connection, and remembers the 'tcpsock' variable on receiving a messageid for login/reg... sorry my code wasn't very revealing because the making a new object and remembering tcpsock is in the script_execute part.

Would this code be a better server loop for receiving?
while (tcp_receive(tcpsock,bytes))
{
    if (byteisheader)
    {
        bytes = read_ushort(tcpsock);
        byteisheader = 0;
    }
    else
    {
        var msgid;
        msgid = read_ubyte(tcpsock);
        chat_add("___msg debug id "+string(msgid));
        if (variable_global_array_get('tcpscript',msgid))
        {
            script_execute(global.tcpscript[msgid]);
        } else {chat_add("___a message id was sent that the server does not know");} //debug
        byteisheader = 1;
        bytes = 2;
    }
}
(tried to be similar to your example).

I still am not sure where to put the tcpsock acceptor definition though...

EDIT: Nevermind for now, I just saved all previous sockets into a loop. I'm not really confident with anything I write in this though. If, when I'm done, I post the source to my first attempt to use this extension could you have a quick look at try and tell me any ineffeciencies or things that just plain don't work (like before with the remote_port thing... I assumed that was what it was for so I just put it in)? It'd be pretty helpful for me and others, as I'll probably make most of the same errors that other people used to 39dll will make.

Edited by jon sploder, 16 February 2012 - 06:37 AM.

  • 0

#126 wnsrn3436

wnsrn3436

    GMC Member

  • GMC Member
  • 189 posts
  • Version:GM:Studio

Posted 16 February 2012 - 12:16 AM

In my opinion. When you upload your source will be processed quickly.
  • 0

#127 jon sploder

jon sploder

    GMC Member

  • GMC Member
  • 919 posts

Posted 16 February 2012 - 07:18 AM

Just checking with you medo, does this seem reasonable:

tcpsock = socket_accept(acceptor);
if (tcpsock >= 0)
{
    sockets += 1;
    socket[sockets] = tcpsock;
    chat_add("new client(socket): "+string(tcpsock)+". total sockets: "+string(sockets));
}
var i;
for (i = 1; i <= sockets; i += 1)
{
    while (tcp_receive(socket[i],bytes))
    {
        chat_add('received a byte');
        if (byteisheader)
        {
            bytes = read_ushort(socket[i]);
            byteisheader = 0;
        }
        else
        {
            var msgid;
            msgid = read_ubyte(socket[i]);
            chat_add("___msg debug id "+string(msgid));
            if (variable_global_array_get('tcpscript',msgid))
            {
                script_execute(global.tcpscript[msgid]);
            } else {chat_add("___a message id was sent that the server does not know");} //debug
            byteisheader = 1;
            bytes = 2;
        }
    }
    //chat_add("for: "+string(i)+" "+string(socket[i]));
}

  • 0

#128 Medo42

Medo42

    GMC Member

  • GMC Member
  • 317 posts

Posted 16 February 2012 - 03:11 PM

Just checking with you medo, does this seem reasonable:

tcpsock = socket_accept(acceptor);
if (tcpsock >= 0)
{
    sockets += 1;
    socket[sockets] = tcpsock;
    chat_add("new client(socket): "+string(tcpsock)+". total sockets: "+string(sockets));
}
var i;
for (i = 1; i <= sockets; i += 1)
{
    while (tcp_receive(socket[i],bytes))
    {
        chat_add('received a byte');
        if (byteisheader)
        {
            bytes = read_ushort(socket[i]);
            byteisheader = 0;
        }
        else
        {
            var msgid;
            msgid = read_ubyte(socket[i]);
            chat_add("___msg debug id "+string(msgid));
            if (variable_global_array_get('tcpscript',msgid))
            {
                script_execute(global.tcpscript[msgid]);
            } else {chat_add("___a message id was sent that the server does not know");} //debug
            byteisheader = 1;
            bytes = 2;
        }
    }
    //chat_add("for: "+string(i)+" "+string(socket[i]));
}

Yes, that looks better, but I still see a problem: byteisheader and bytes are shared between all sockets, so if the last thing read from one socket is the header (again, unlikely on a LAN) the next socket will attempt to receive a message corresponding to the header of the previous socket. To make this work, you need byteisheader and bytes for every socket. In Gang Garrison 2 every client has its own object (Player) which holds the socket and the current protocol state (commandReceiveState and commandReceiveExpectedBytes - basically the same as your byteisheader and bytes). I do realize that it's awkward to manually keep track of this state, and that it is something many games need. Even 39dll has some support for automating this with its message formats, so this is definitely something I want to address some time. No promises on when that will happen though.

Edited by Medo42, 16 February 2012 - 03:12 PM.

  • 0

#129 jon sploder

jon sploder

    GMC Member

  • GMC Member
  • 919 posts

Posted 19 February 2012 - 02:40 AM

Thanks.

write_buffer_to_file() doesn't work with sockets as the buffer for me. Can you replicate this? And/Or buffer_size doesn't work with sockets. My code to show this is:

var buffer;
buffer = buffer_create();

write_ubyte(buffer,8);
write_string_delim(buffer,argument1);
chat_add("buffer size: "+string(buffer_size(argument0)));
buffer_clear(argument0);
write_ushort(argument0,buffer_size(buffer));
write_buffer(argument0,buffer);
chat_add("buffer size: "+string(buffer_size(argument0)));
socket_send(argument0);

buffer_destroy(buffer);
Both times it returns a buffer size of 0. See if you can replicate this yourself.

Edited by jon sploder, 19 February 2012 - 02:49 AM.

  • 0

#130 wnsrn3436

wnsrn3436

    GMC Member

  • GMC Member
  • 189 posts
  • Version:GM:Studio

Posted 19 February 2012 - 10:08 AM

Thanks.

write_buffer_to_file() doesn't work with sockets as the buffer for me. Can you replicate this? And/Or buffer_size doesn't work with sockets. My code to show this is:

var buffer;
buffer = buffer_create();

write_ubyte(buffer,8);
write_string_delim(buffer,argument1);
chat_add("buffer size: "+string(buffer_size(argument0)));
buffer_clear(argument0);
write_ushort(argument0,buffer_size(buffer));
write_buffer(argument0,buffer);
chat_add("buffer size: "+string(buffer_size(argument0)));
socket_send(argument0);

buffer_destroy(buffer);
Both times it returns a buffer size of 0. See if you can replicate this yourself.


The socket is different from the common buffer.
The socket send/receive buffers is divided.
In my opinion. You should use the following functions.

socket_sendbuffer_size(socket) : size
socket_receivebuffer_size(socket) : size

Edited by wnsrn3436, 19 February 2012 - 10:09 AM.

  • 0

#131 Medo42

Medo42

    GMC Member

  • GMC Member
  • 317 posts

Posted 19 February 2012 - 02:05 PM

I can see that I made some mistakes there. First, I don't think there's an actual reason why write_buffer_to_file() shouldn't work on sockets (though if you look at the documentation, it explicitly says "buffer", so it's at least consistent).

Second, it was possibly not a good idea at all to have the write/read functions accept sockets directly, because this makes sockets work in some places where buffers work, but not in others where you might expect it, and where it does work the semantics are slightly different, all of which makes this feature somewhat confusing. It is easier in some ways, but more complex. And while it does save some code in small examples, if I consider Gang Garrison most of the networking uses explicit buffers anyway or could be changed to use them with very little extra code.

Other things that I'm considering to change include
- the lenient behavior regarding e.g. invalid handles (replaced by some way of logging / error handling)
- the names of the read/write functions to reduce confusion of type lenght (read_ushort->read_u16, read_double->read_f64)
- remove the buffer endian awareness (replaced by explicit-endian read/write functions, e.g. read_u16l, read_u16b). Not sure what to make the default there; big-endian is the "standard" network byte order, but little-endian is what 39dll does. Maybe there could be no default at all and you always have to specify l/b... at least it would force people to consider what they are doing, and if you stick with one standard it shouldn't take any mental effort to always add the same suffix.

Don't worry, those are just ideas for version 2, aka the great big redesign. If/when I get around to working on that, version 1 will still be supported.
  • 0

#132 Ronchon le Nain

Ronchon le Nain

    GMC Member

  • GMC Member
  • 143 posts
  • Version:GM8

Posted 19 February 2012 - 08:18 PM

Greatings.
I'm now testing Faucet. Congratulations for the help file, it's really helpfull and well explained.
One small question : is there an equivalent to buffer_bytes_left , but for the receive buffer of a socket, to know how many bytes are still remaining to be read ?
  • 0
www.ymir-online.eu
Posted Image

#133 jon sploder

jon sploder

    GMC Member

  • GMC Member
  • 919 posts

Posted 19 February 2012 - 09:11 PM

Thanks wrsrn and Medo, yeah I saw that it said explicitly buffer but assumed because some other buffer functions can be substituted successfully that it might be do-able. Those ideas seem interesting.
  • 0

#134 Medo42

Medo42

    GMC Member

  • GMC Member
  • 317 posts

Posted 19 February 2012 - 09:59 PM

Greatings.
I'm now testing Faucet. Congratulations for the help file, it's really helpfull and well explained.
One small question : is there an equivalent to buffer_bytes_left , but for the receive buffer of a socket, to know how many bytes are still remaining to be read ?

Thanks. I just checked in the code, there isn't. It's probably just something that never came up before.
I just changed the code around a bit so that buffer_bytes_left, buffer_set_readpos, write_buffer_to_file and append_file_to_buffer now all accept sockets too. This means udp_send is one of the few functions remaining which accept buffers but not all sockets - unfortunately, it already has a particular meaning for being called on udp sockets with very different semantics. How did I get the idea again that this kind of punning was a good idea? :P
The only other remaining functions which don't accept sockets are those where the meaning would be unclear: buffer_destroy, buffer_clear and buffer_size.

Edited by Medo42, 19 February 2012 - 10:00 PM.

  • 0

#135 Medo42

Medo42

    GMC Member

  • GMC Member
  • 317 posts

Posted 19 February 2012 - 10:22 PM

Here you go: Version 1.4
- Enabled more buffer functions to use sockets: buffer_bytes_left, buffer_set_readpos, write_buffer_to_file and append_file_to_buffer

Download
  • 0

#136 wnsrn3436

wnsrn3436

    GMC Member

  • GMC Member
  • 189 posts
  • Version:GM:Studio

Posted 19 February 2012 - 11:53 PM

Right.
I think it would eliminate the confusion that feature.
The new version is very good.

Also, I should hope.
1. I think you need to add more files to function.
2. Encryption function of the buffer.
3. Whether the message is received. (function).
4. Get my ip. (Lan ip, Wan ip, ipv4, ipv6).
5. Get my hostname.

In the case of 3. it useful for sending exit messages.
The most similar code :
write_byte(socket, 0)
socket_send(socket)
while(socket_sendbuffer_size(socket)){} // Whether the message is received.
socket_destroy(socket)

In the case of 4.
The most similar code *Script* get_my_ip(0=Lan ip/1=Wan ip) :
/*Do not use it!
Obtaining WAN IP is unstable.*/
if !argument0{return mplay_ipaddress()} //Lan ip
else
{
    var skv_soket, skv_string, skv_buffer;
    skv_soket=tcp_connect("whatismyip.org", 80)
    if socket_has_error(skv_soket){socket_destroy(skv_soket); return ""}
    write_string(skv_soket, "GET / HTTP/1.0"+chr(13)+chr(10)+chr(13)+chr(10))
    socket_send(skv_soket)
    skv_buffer=buffer_create()
    while(!tcp_eof(skv_soket))
    {
        sleep(10)
        tcp_receive_available(skv_soket)
        write_buffer(skv_buffer, skv_soket)
    }
    skv_string=read_string(skv_buffer, buffer_size(skv_buffer))
    skv_string=string_delete(skv_string, 1, string_pos(chr(13)+chr(10), skv_string)+2)
    skv_string=string_delete(skv_string, 1, string_pos(chr(13)+chr(10), skv_string)+2)
    socket_destroy(skv_soket)
    buffer_destroy(skv_buffer)
    return skv_string
    //Wan ip
}

Edited by wnsrn3436, 03 March 2012 - 08:40 AM.

  • 0

#137 Medo42

Medo42

    GMC Member

  • GMC Member
  • 317 posts

Posted 20 February 2012 - 08:02 AM

1. I think you need to add more files to function.
2. Encryption function of the buffer.
3. Whether the message is received. (function).
4. Get my ip. (Lan ip, Wan ip, ipv4, ipv6).
5. Get my hostname.

In the case of 3. it useful for sending exit messages.
Methods now available. :

while(socket_sendbuffer_size(socket)){}

You think I need to? That... doesn't quite sound right.

Anyway, as for 1 and 2 I'd prefer not to add more features not directly related to networking and rather address these things in version 2, which will support some standard for efficiently handing binary data from one extension to another, so that this kind of functionality can be provided by other extensions.

As for 3, this is something the socket API simply does not provide as far as I am aware. However, a small extension to the semantics of socket_sendbuffer_size() would allow you to determine if everything has at least been sent in the way you show. Actually what it would indicate is even weaker, i.e. if everything has been written to the operating system for sending, but that should be enough to ensure everything is sent on exit. Currently, this approach might work, but I'd have to make sure and make the specification more clear on that point.

4. is partially covered by socket_local_ip. Note that your machine can have several IPs, so just asking for "the LAN IP" does not always have a single answer. There is a way to enumerate the IP configuration of the networking adapters, but I think there is simply not much need for providing it (please give a specific example of what you want to achieve). There is no lowlevel way to determine your WAN IP.

5. That would be trivial to add, but what is it needed for?
  • 0

#138 wnsrn3436

wnsrn3436

    GMC Member

  • GMC Member
  • 189 posts
  • Version:GM:Studio

Posted 20 February 2012 - 01:43 PM

4. is partially covered by socket_local_ip. Note that your machine can have several IPs, so just asking for "the LAN IP" does not always have a single answer. There is a way to enumerate the IP configuration of the networking adapters, but I think there is simply not much need for providing it (please give a specific example of what you want to achieve). There is no lowlevel way to determine your WAN IP.

5. That would be trivial to add, but what is it needed for?


​I'm just trying to get IP address with lookup function using hostname​. :sweat:

Ps. socket_local_ip->socket_remote_ip?

Edited by wnsrn3436, 20 February 2012 - 01:44 PM.

  • 0

#139 Medo42

Medo42

    GMC Member

  • GMC Member
  • 317 posts

Posted 21 February 2012 - 07:37 PM

​I'm just trying to get IP address with lookup function using hostname​. :sweat:

Well, I guess that's a way to do this :P

Ps. socket_local_ip->socket_remote_ip?

I'll be happy to answer questions, but please make the effort of asking them in plain english.
  • 0

#140 jon sploder

jon sploder

    GMC Member

  • GMC Member
  • 919 posts

Posted 27 February 2012 - 07:37 AM

Medo, I'm trying to write a script that passes a message on to all other clients, ie ClientA -> Server, Server -> Client[B,C,D...].
My script: (for now, only assume tcp, I'm not using udp but that's old code just for when I do include both protocols)

/*
pass_packet(all | individual_socket,source_socket,protocol,ip);
argument0: all being 0, otherwise an individual socket is assumed (tcp or udp)
argument1: the socket or buffer being copied
argument2: udp/tcp, udp being 0, tcp being 1
argument3: if script is not called from obj_player or obj_server, this argument specifies ip for individual_socket
*/

if (argument3 != "")
{
    var ip;
    ip = argument3;
}

//pass on to all other players
if (argument0 == 0)
{
    with (obj_player)
    {
        if (id != other.id) //I know this won't work locally, but I've taken that into consideration when testing
        {
            if (argument2) //send tcp
            {
                chat_add("passing a tcp packet from "+string(argument1)+" to "+string(tcpsock));
                //write size of packet bytes and then message id etc.
                write_buffer(tcpsock,argument1);
                socket_send(tcpsock);
            }
            else //send udp
            {
                udp_send(argument1,ip,global.udpport);
            }
        }
    }
}
//pass on to an individual socket
else
{
    if (argument2) //send tcp
    {
        write_buffer(argument0,argument1);
        socket_send(argument0);
    }
    else //send udp
    {
        udp_send(argument1,ip,global.udpport);
    }
}

I know this isn't correct, I wrote it a while ago (several pages of explanations ago), but I'm not sure how I should write it after the 'if (argument2) //send tcp' segment... could you help me out with an example script to pass a packet on from one players receiving tcp socket, to another players sending tcp socket.
  • 0

#141 Medo42

Medo42

    GMC Member

  • GMC Member
  • 317 posts

Posted 27 February 2012 - 07:50 PM

First, it would be very helpful to know the actual problem. What do you want to happen, and what actually does happen? It's not clear to me what the problem is from just your short explanation and looking at the code. You say you don't know what to write at a point that actually occurs twice in the code, and yet you wrote something there - so what's wrong with what you wrote?

Second, if you have relatively general questions that might not require deep knowledge of the extension, you can try asking them on StackOverflow. That could get you answers or at least ideas more quickly, and if nothing is forthcoming you can still get back here and just post a link to the question (so I can get all the delicious reputation points, hehe).
  • 0

#142 jon sploder

jon sploder

    GMC Member

  • GMC Member
  • 919 posts

Posted 28 February 2012 - 06:34 AM

This is quite specific to faucet networking;
I'd like a script to pass a packet received to many other sockets without having to make an individual script for each message id, or type of packet. For instance instead of writing, to pass a chat message;
message = read_string_delim(tcpsock);
chat_add(string(id)+": PARSED CHAT PACKET: "+message);

file = file_text_open_append(working_directory+"\Chatlog.txt");
file_text_write_string(file,name+": "+message);
file_text_writeln(file);
file_text_close(file);

buffer = buffer_create();
write_ubyte(buffer,8);
write_ubyte(buffer,position);
write_string_delim(buffer,message)

with (obj_player)
{
    if (id != other.id)
    {
        buffer_clear(tcpsock);
        write_ushort(tcpsock,buffer_size(other.buffer));
        write_buffer(tcpsock,other.buffer);
        socket_send(tcpsock);
    }
}
buffer_destroy(buffer);
I'd rather just call a script to pass this, ie: pass_packet(0,tcpsock,1,"");

It sounds stupid to ask for a script without any code given, which is why I posted my code (I have actually tried to make the script myself, but unsuccessfully and I'm just not sure why):
The rough script I was thinking of would go something like this:
with (other players)
{
                buffer_clear(tcpsock);
                write_ushort(tcpsock,buffer_size(argument1));
                write_ubyte(tcpsock,msgid);
                write_buffer(tcpsock,argument1);
                socket_send(tcpsock);
}
I've had many problems with this, the most recent try results in the client receiving a message id (the ubyte msgid line) of 0, when it's actually 10. There are probably more problems, but because I have almost no-one who is willing to test this for me I have to work around it by writing code for a player sending it back to himself. Obviously there are problems with this idea as clearing the buffer screws things up, but I worked around it. I'm just not sure the correct code to do this and don't have someone I can constantly send .gm81's to test with, modifying every single variable to figure out how to do it.

Edited by jon sploder, 28 February 2012 - 06:35 AM.

  • 0

#143 Medo42

Medo42

    GMC Member

  • GMC Member
  • 317 posts

Posted 01 March 2012 - 12:37 AM

It sounds stupid to ask for a script without any code given, which is why I posted my code (I have actually tried to make the script myself, but unsuccessfully and I'm just not sure why)

I wasn't complaining that you provided code - that's a good thing, and shows that you are putting effort into figuring this out. It just wasn't clear to me what exactly the code was supposed to do, and how that differs from what actually happens.

Now let's see if I can help...

with (obj_player)
{
    if (id != other.id)
    {
        buffer_clear(tcpsock);
        write_ushort(tcpsock,buffer_size(other.buffer));
        write_buffer(tcpsock,other.buffer);
        socket_send(tcpsock);
    }
}

buffer_clear does nothing on sockets. I suspect this is a habit from the use of 39dll with a default buffer, where you have to clear that buffer first, then prepare your data in it and then copy it to the socket. The difference in FN is that socket_send does not make a copy of some buffer, it marks everything written to the socket so far as "ready for sending". In other words, everything written to a FN tcpSocket is sent exactly as it was written - socket_send only determines when that happens.

I'd rather just call a script to pass this, ie: pass_packet(0,tcpsock,1,"");
The rough script I was thinking of would go something like this:

with (other players)
{
                buffer_clear(tcpsock);
                write_ushort(tcpsock,buffer_size(argument1));
                write_ubyte(tcpsock,msgid);
                write_buffer(tcpsock,argument1);
                socket_send(tcpsock);
}


If your message format is still the same as in the example you posted a while ago, you need to add 1 to the message length, since you add the msgid seperately. Something else to look out for is that buffer_size only works for buffers as was already discussed (and yes, that's my fault :P).

However, you can work around that in several ways. For example, you can copy the message to an actual buffer first:
msgbuffer = buffer_create();
write_ubyte(msgbuffer, msgid);
write_buffer(msgbuffer,argument1);
with (other players)
{
                write_ushort(tcpsock,buffer_size(msgbuffer));
                write_buffer(tcpsock,msgbuffer);
                socket_send(tcpsock);
}
buffer_destroy(msgbuffer);

Alternatively, you can use
max(socket_receivebuffer_size(argument1), buffer_size(argument1))
to determine either the size of a buffer or the size of the receivebuffer of a socket - each function returns 0 if the wrong type of object handle is passed, so the maximum will be the result of the function that worked.

This is definitely an ugly spot in the API, but I can't think of a good solution right now that doesn't break compatibility. The best idea so far is to make socket_receivebuffer_size work on buffers as well, but that does not fit the function name well.

Edited by Medo42, 01 March 2012 - 12:37 AM.

  • 0

#144 wnsrn3436

wnsrn3436

    GMC Member

  • GMC Member
  • 189 posts
  • Version:GM:Studio

Posted 01 March 2012 - 11:23 AM

What is plan of version 2?
I expect that version.

My point of view, the buffer should have features similar to ds_list. ;)
For example.
buffer_insert (ds_list_insert), buffer_replace, buffer_delete.
You think all of this, should consider.

And I have a ideas for a bit function.
Variable=1234
Variable=new_bit_function(Variable)
Results. Variable="10011010010"

What about you?

Ps. My FNs script, what do you think? (FN Simple Scripts)

Edited by wnsrn3436, 01 March 2012 - 11:38 AM.

  • 0

#145 jon sploder

jon sploder

    GMC Member

  • GMC Member
  • 919 posts

Posted 05 March 2012 - 06:53 AM

Alright, thanks Medo that solves a few problems and shortened some code, but I've just got such a massive amount of problems that seem endless, I decided to upload the source online. All the program is so far is chat + login/reg and primitive drawing of lines (mouse). The problem being is that when I test locally I can't replicate half the bugs experienced when not local.

The problems I'd really appreciate you take a quick look at:
Is my general online method correct for non-local testing?
Why does my pass_packet script continue to screw up? You can see that it doesn't screw up if, you take the 'drawkit' script in the Server.gm81 and uncomment the code commented, and comment out the pass_packet 1st line.
Anything online with tcp.

What I don't care about at all:
Problems within the actual chat system drawing etc, or line drawing (drawkit offline stuff).
Anything offline.
UDP (it's not really implemented anyway)

My sources: http://www.box.com/s...nb18mico4npo113
  • 0

#146 Medo42

Medo42

    GMC Member

  • GMC Member
  • 317 posts

Posted 12 March 2012 - 09:25 PM

What is plan of version 2?
I expect that version.

Don't. So far it's just a collection of ideas, not completely thought through yet, and at this point I won't even promise that it will eventually be made (though I still plan to do it).

Alright, thanks Medo that solves a few problems and shortened some code, but I've just got such a massive amount of problems that seem endless, I decided to upload the source online. All the program is so far is chat + login/reg and primitive drawing of lines (mouse). The problem being is that when I test locally I can't replicate half the bugs experienced when not local.

The problems I'd really appreciate you take a quick look at:
Is my general online method correct for non-local testing?
Why does my pass_packet script continue to screw up? You can see that it doesn't screw up if, you take the 'drawkit' script in the Server.gm81 and uncomment the code commented, and comment out the pass_packet 1st line.
Anything online with tcp.

What I don't care about at all:
Problems within the actual chat system drawing etc, or line drawing (drawkit offline stuff).
Anything offline.
UDP (it's not really implemented anyway)

My sources: http://www.box.com/s...nb18mico4npo113

The thing is, I will not help you tackle a massive ammount of problems that seems endless. I have my own problems to work on, and while I can spare some time to answer the occasional question, I will not spend hours digging into your code to find unspecified bugs.

To help you isolate problems in networking code, in particular in non-local testing, I can once again point you to Wireshark. That tool allows you to capture the network traffic from and to your PC, so that you can observe exactly what happens in your connections and narrow down where a particular bug might be.
  • 0

#147 Medo42

Medo42

    GMC Member

  • GMC Member
  • 317 posts

Posted 12 March 2012 - 09:39 PM

Version 1.4.1 has been released.

It fixes a crash and potential security vulnerability triggered by using game_restart(). It is strongly recommended to update to this new version.

Download here.
  • 0

#148 jon sploder

jon sploder

    GMC Member

  • GMC Member
  • 919 posts

Posted 13 March 2012 - 06:45 AM

By the way; I direct that post to everyone viewing, rather than just Medo.
  • 0

#149 Ronchon le Nain

Ronchon le Nain

    GMC Member

  • GMC Member
  • 143 posts
  • Version:GM8

Posted 13 March 2012 - 11:29 PM

I have a weird bug where i have a message (A) that is not processed ( whether if he's not sent by client or not received by server is unclear ) until a 2nd one of any other kind (B) is sent, whatever the delay between them.
Meaning if i send A only, nothing happens : nothing is ever received by server.
If i send A twice, 2 A are areceived.
If i send A then B , A then B are received.
If i send B , B is received...
I have no idea of what's going on. It's like my first A message is queued up somewhere, but only when it's a A.

Posted Image

Edited by Ronchon le Nain, 15 March 2012 - 07:09 PM.

  • 0
www.ymir-online.eu
Posted Image

#150 Medo42

Medo42

    GMC Member

  • GMC Member
  • 317 posts

Posted 15 March 2012 - 11:17 PM

I have a weird bug where i have a message (A) that is not processed ( whether if he's not sent by client or not received by server is unclear ) until a 2nd one of any other kind (B) is sent, whatever the delay between them.
Meaning if i send A only, nothing happens : nothing is ever received by server.
If i send A twice, 2 A are areceived.
If i send A then B , A then B are received.
If i send B , B is received...
I have no idea of what's going on. It's like my first A message is queued up somewhere, but only when it's a A.

Posted Image

The extension definitely doesn't look at the content of your messages, so if A and B are the same length there must be something different in your code between them. Once again I recommend Wireshark to find out when A is actually sent, to see whether the problem is on the sending or the receiving end.
  • 0




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users