PDA

View Full Version : Add new function to libcod on windows



Kantin
8th January 2015, 16:10
Hey there,

I want to add my own function to libcod (win version). Its an udp socket communication stuff, and its working in a c console application. But after I copied source inside the dll source, there is no error I can compile it, its not working. Socket is fine I can store data on it from gsc and the other side get it, but the receiver function not working. i = recvfrom(Socket, response, strlen(response), 0, 0, 0); (Socket is the socket and the response is a char array)i value is -1 after the call (i should represent the number of the bytes received), like I sad the console app stuff working fine with the same source, so I guess the source is fine. Any idea?

Thanks in advance!

Mitch
8th January 2015, 17:29
Can you post your changes? I can't say what is wrong with so little code. You could use Wireshark to see if your message gets send.
Also you could try debugging your function with printf (for Windows you need Com_printf).

If you haven't added your own function yet see this thread: http://killtube.org/showthread.php?2083-Tutorial-How-to-create-your-own-libcod-function

Kantin
8th January 2015, 18:28
Sure i can, so i append


{"foo" , gsc_utils_foo , 0},

to the Scr_Function scriptFunctions[] array in gsc.cpp, I added also the prototype to the header file.

and this to gsc_utils.cpp


void gsc_utils_foo() {
char *str;
char *ip;
int *port;
char data[9] = {"123456789"}; //test data or whatever

if ( ! stackGetParams((char *)"s", &str) || strlen(str) == 0) {
stackPushUndefined();
return;
}
ip = strtok(strdup(str), ",");
if(!isValidNumber(strtok(NULL, ","), &port))
{
stackPushInt(0);
}

if(isValidAddress(ip) && port != NULL)
{
stackPushString(foo(ip, (int)port), data);
}
else
{
stackPushInt(0);
}
}

char* foo(char *ip, int port, char *data)
{
SOCKET Socket;
WSADATA WSAData = {0};
SOCKADDR_IN server;
struct timeval timeout;

int i;
char response[1024];

server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = inet_addr(ip);

timeout.tv_sec = 5;
timeout.tv_usec = 0;

if(WSAStartup (MAKEWORD(2, 2), &WSAData))
{
return "0";
}

Socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(Socket == INVALID_SOCKET)
{
WSACleanup();
return "0";
}

if(setsockopt(Socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)) < 0)
{
closesocket(Socket);
WSACleanup();
return "0";
}

if(setsockopt(Socket, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout)) < 0)
{
closesocket(Socket);
WSACleanup();
return "0";
}

if(connect(Socket, (SOCKADDR *)&server, sizeof(server)) == SOCKET_ERROR)
{
closesocket(Socket);
WSACleanup();
return "0";
}

sendto(Socket, data, strlen(data), 0, (SOCKADDR *)&server, sizeof(server));
i = recvfrom(Socket, response, strlen(response), 0, 0, 0);

if(i < 0)
{
closesocket(Socket);
WSACleanup();
return "0";
}

response[i] = '\0';

closesocket(Socket);
WSACleanup();
return response;
}

and inside foo func the sendto func works fine and the recvfrom not working.

Mitch
8th January 2015, 19:03
How do you call your function in cod2?

Like this

foo("1.1.1.1", 28962);



int port;
char *srt;

if ( ! stackGetParams("si", &srt, &port)) { // fetches first parameter as int, 2nd as string
stackPushUndefined();
return;
}

Kantin
8th January 2015, 19:29
in cod


foo("1.1.1.1,28960")


and your method is better, not necessary to convert string to int but this part already working with my case

Mitch
8th January 2015, 20:11
Your message should show up in wireshark. (https://www.wireshark.org/)

Try this. With the output you should be able to see if it worked.


void gsc_utils_foo() {
char *ip;
int port;
char data[9] = {"123456789"}; //test data or whatever

if ( ! stackGetParams((char *)"si", &ip, &port)) {
stackPushUndefined();
return;
}

if(isValidAddress(ip))
{
stackPushString(foo(ip, port, data));
}
else
{
stackPushInt(0);
}
}

char* foo(char *ip, int port, char *data)
{
SOCKET Socket;
WSADATA WSAData = {0};
SOCKADDR_IN server;
struct timeval timeout;

int i;
char response[1024];

Com_Printf("IP: %s\n", ip);
Com_Printf("Port: %d\n", port);
Com_Printf("Data: %s\n", data);

server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = inet_addr(ip);

timeout.tv_sec = 5;
timeout.tv_usec = 0;

if(WSAStartup (MAKEWORD(2, 2), &WSAData))
{
return "0";
}

Socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(Socket == INVALID_SOCKET)
{
WSACleanup();
return "0";
}

if(setsockopt(Socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)) < 0)
{
closesocket(Socket);
WSACleanup();
return "0";
}

if(setsockopt(Socket, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout)) < 0)
{
closesocket(Socket);
WSACleanup();
return "0";
}

if(connect(Socket, (SOCKADDR *)&server, sizeof(server)) == SOCKET_ERROR)
{
closesocket(Socket);
WSACleanup();
return "0";
}

sendto(Socket, data, strlen(data), 0, (SOCKADDR *)&server, sizeof(server));
i = recvfrom(Socket, response, strlen(response), 0, 0, 0);
Com_Printf("Response: %s [%d]\n", response, i);

if(i < 0)
{
closesocket(Socket);
WSACleanup();
return "0";
}

response[i] = '\0';

closesocket(Socket);
WSACleanup();
return response;
}

IzNoGoD
8th January 2015, 20:26
Shouldnt data be terminated by \0? There is no room for it in the [9].

Kantin
8th January 2015, 22:40
The specified server what u can set ingame (with ip and port) get the data after the sendto so thats correct

Kantin
9th January 2015, 00:35
Yes, in wireshark I can see the sent data and after that, the next row is the response with the correct data, but in the dll the response variable still memory trash and the recvfrom func returned value still -1

kung foo man
9th January 2015, 04:43
To make sure this is not a non-blocked-recvfrom() issue, can you try:



sendto(Socket, data, strlen(data), 0, (SOCKADDR *)&server, sizeof(server));
Sleep(1000); // just to wait till there is really some data send back from your "server"
i = recvfrom(Socket, response, strlen(response), 0, 0, 0);

Kantin
9th January 2015, 13:03
I tried this too, but the same issue with one sec connection interupted after call :D

kung foo man
9th January 2015, 18:35
You could just add an error check:



i = recvfrom(Socket, response, sizeof(response), 0, 0, 0);
if (i == SOCKET_ERROR) {
Com_Printf("recvfrom failed with error %d\n", WSAGetLastError());
stackPushString("hurrdurr");
}


Would print: recvfrom failed with error 10040

Google: http://support.ipswitch.com/kb/WSK-19980714-EM13.htm



Question/Problem: Message too long.
Answer/Solution: A message sent on a socket was larger than the internal message buffer or some other network limit.


Check the size of your buffer:



char response[1024];
i = recvfrom(Socket, response, strlen(response), 0, 0, 0);


Realize that strlen(response) is always 0, since empty arrays are initialized to zero.

Set the size with sizeof() instead and it will work:



i = recvfrom(Socket, response, sizeof(response), 0, 0, 0);

Kantin
9th January 2015, 21:07
Thank you man! Its working strlen was a bad choice, with sizeof its ok!!