buldthensnip/lua_net.h

389 lines
8.0 KiB
C

/*
This file is part of Iceball.
Iceball is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Iceball is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Iceball. If not, see <http://www.gnu.org/licenses/>.
*/
/*
str = common.net_pack(fmt, ...)
packs data into a string
format is as such:
b/B = signed/unsigned 8-bit
h/H = signed/unsigned 16-bit
i/I = signed/unsigned 32-bit
f = single-precision 32-bit float
d = double-precision 64-bit float
z = zero-terminated string
#s = fixed-length string (replace # with a decimal number)
..., remain = common.net_unpack(fmt, str)
unpacks data from a string
will attempt to decode from start to end
"remain" is the remainder of the string which was not decoded
returns nil for fields that could not be decoded
*/
int icelua_fnaux_net_packlen(lua_State *L, const char *fmt, int top)
{
const char *v = fmt;
int p = 2;
int n = 0;
int len = 0;
while(*v != '\0')
{
if(*v >= '0' && *v <= '9')
{
n = n*10 + (*(v++) - '0');
} else switch(*(v++)) {
case 'b':
case 'B':
p++;
len += 1;
break;
case 'h':
case 'H':
p++;
len += 2;
break;
case 'i':
case 'I':
p++;
len += 4;
break;
case 'f':
p++;
len += 4;
break;
case 'd':
p++;
len += 8;
break;
case 's':
p++;
len += n;
n = 0;
break;
case 'z':
if(p <= top && lua_isstring(L, p))
len += strlen(lua_tostring(L, p))+1;
else
len += 1;
p++;
break;
default:
fprintf(stderr, "net pack format: unexpected char\n");
return -1;
}
}
return len;
}
int icelua_fn_common_net_pack(lua_State *L)
{
int top = icelua_assert_stack(L, 1, 99999);
const char *fmt = lua_tostring(L, 1);
if(fmt == NULL)
return luaL_error(L, "not a string");
const char *v = fmt;
int p = 2;
int n = 0;
int xint;
float xfloat;
double xdouble;
int slen = icelua_fnaux_net_packlen(L, fmt, top);
if(slen < 0)
return luaL_error(L, "invalid pack format");
char *sbuf = malloc(slen+1);
// TODO: check if NULL
char *sstop = sbuf+slen;
*sstop = '\0';
char *s = sbuf;
while(*v != '\0')
{
if(*v >= '0' && *v <= '9')
{
n = n*10 + (*(v++) - '0');
} else switch(*(v++)) {
case 'b':
case 'B':
xint = lua_tointeger(L, p++);
*(s++) = xint & 0xFF;
break;
case 'h':
case 'H':
xint = lua_tointeger(L, p++);
*(s++) = xint & 0xFF;
*(s++) = (xint>>8) & 0xFF;
break;
case 'i':
case 'I':
xint = lua_tointeger(L, p++);
*(s++) = xint & 0xFF;
*(s++) = (xint>>8) & 0xFF;
*(s++) = (xint>>16) & 0xFF;
*(s++) = (xint>>24) & 0xFF;
break;
case 'f':
xfloat = lua_tointeger(L, p++);
xint = *(int *)(float *)&xfloat;
*(s++) = xint & 0xFF;
*(s++) = (xint>>8) & 0xFF;
*(s++) = (xint>>16) & 0xFF;
*(s++) = (xint>>24) & 0xFF;
break;
case 'd':
xdouble = lua_tointeger(L, p++);
xint = ((int *)(float *)&xdouble)[0];
*(s++) = xint & 0xFF;
*(s++) = (xint>>8) & 0xFF;
*(s++) = (xint>>16) & 0xFF;
*(s++) = (xint>>24) & 0xFF;
xint = ((int *)(float *)&xdouble)[4];
*(s++) = xint & 0xFF;
*(s++) = (xint>>8) & 0xFF;
*(s++) = (xint>>16) & 0xFF;
*(s++) = (xint>>24) & 0xFF;
break;
case 's': {
size_t slen;
const char *xstr = lua_tolstring(L, p++, &slen);
memset(s, 0, n);
if(xstr != NULL)
memcpy(s, xstr, ((int)slen <= n ? (int)slen : n));
s += n;
n = 0;
} break;
case 'z': {
const char *xstr = lua_tostring(L, p++);
if(xstr != NULL)
{
int slen = strlen(xstr);
memcpy(s, xstr, slen);
s += slen;
}
*(s++) = '\0';
} break;
default:
fprintf(stderr, "net_pack[EDOOFUS]: unexpected char\n");
fflush(stderr);
abort();
}
}
if(s != sstop)
{
//fprintf(stderr, "%i\n", (int)(sstop-s));
fprintf(stderr, "net_pack[EDOOFUS]: s != sstop!\n");
fflush(stderr);
abort();
}
lua_pushlstring(L, sbuf, slen);
free(sbuf);
return 1;
}
int icelua_fn_common_net_unpack(lua_State *L)
{
int top = icelua_assert_stack(L, 2, 2);
const char *fmt = lua_tostring(L, 1);
if(fmt == NULL)
return luaL_error(L, "not a string");
size_t bsize;
const char *str = lua_tolstring(L, 2, &bsize);
if(str == NULL)
return luaL_error(L, "not a string");
const char *v = fmt;
const char *s = str;
int p = 0;
int n = 0;
int xint;
float xfloat;
double xdouble;
while(*v != '\0')
{
if(*v >= '0' && *v <= '9')
{
n = n*10 + (*(v++) - '0');
} else switch(*(v++)) {
case 'b':
xint = *(int8_t *)s;
s++;
lua_pushinteger(L, xint);
p++;
break;
case 'B':
xint = *(uint8_t *)s;
s++;
lua_pushinteger(L, xint);
p++;
break;
case 'h':
xint = *(int16_t *)s;
s += 2;
lua_pushinteger(L, xint);
p++;
break;
case 'H':
xint = *(uint16_t *)s;
s += 2;
lua_pushinteger(L, xint);
p++;
break;
case 'i':
xint = *(int32_t *)s;
s += 4;
lua_pushinteger(L, xint);
p++;
break;
case 'I':
xint = *(uint32_t *)s;
s += 4;
lua_pushinteger(L, xint);
p++;
break;
case 'f':
xfloat = *(float *)s;
s += 4;
lua_pushnumber(L, xfloat);
p++;
break;
case 'd':
xdouble = *(double *)s;
s += 8;
lua_pushnumber(L, xdouble);
p++;
break;
case 's': {
lua_pushlstring(L, s+n, n);
p++;
break;
} break;
case 'z': {
int slen = strlen(s);
lua_pushstring(L, s);
s += slen+1;
p++;
break;
} break;
default:
lua_pop(L, p);
return luaL_error(L, "net_unpack: unexpected char\n");
}
}
lua_pushlstring(L, s, (size_t)(bsize-(int)(s-str)));
return p+1;
}
int icelua_fn_common_net_send(lua_State *L)
{
int top = icelua_assert_stack(L, 2, 2);
size_t bsize;
const char *str = lua_tolstring(L, 2, &bsize);
if(str == NULL)
return luaL_error(L, "not a string");
// TODO: incorporate the sockfd field
//net_packet_push(int len, uint8_t *data, packet_t **head, packet_t **tail);
if(L != lstate_server)
{
net_packet_push_lua((int)bsize, str, to_client_local.sockfd,
&(to_client_local.send_head), &(to_client_local.send_tail));
lua_pushboolean(L, 1);
return 1;
} else {
int sockfd = -1;
if(lua_isboolean(L, 1) && lua_toboolean(L, 1))
{
sockfd = -2;
} else if(lua_isnumber(L, 1)){
sockfd = lua_tonumber(L, 1);
if(sockfd < 0)
sockfd = -1;
}
if(sockfd == -1)
return 0;
net_packet_push_lua((int)bsize, str, sockfd, &(to_server.send_head), &(to_server.send_tail));
lua_pushboolean(L, 1);
return 1;
}
}
int icelua_fn_common_net_recv(lua_State *L)
{
int top = icelua_assert_stack(L, 0, 0);
if(L == lstate_server)
{
packet_t *pkt = net_packet_pop(&(to_server.head), &(to_server.tail));
if(pkt == NULL)
return 0;
if(pkt->data[0] >= 0x40 && pkt->data[0] <= 0x7E)
lua_pushlstring(L, &pkt->data[1], pkt->len-1);
else if(pkt->data[0] == 0x7F)
lua_pushlstring(L, &pkt->data[3], pkt->len-3);
else {
fprintf(stderr, "EDOOFUS: SYSTEM PACKET *MUST NOT* REACH common.net_recv!\n");
fflush(stderr);
abort();
}
if(pkt->sockfd == -1)
lua_pushnil(L);
else if(pkt->sockfd < 0)
lua_pushboolean(L, 1);
else
lua_pushinteger(L, pkt->sockfd);
return 2;
} else {
packet_t *pkt = net_packet_pop(&(to_client_local.head), &(to_client_local.tail));
if(pkt == NULL)
return 0;
if(pkt->data[0] >= 0x40 && pkt->data[0] <= 0x7E)
lua_pushlstring(L, &pkt->data[1], pkt->len-1);
else if(pkt->data[0] == 0x7F)
lua_pushlstring(L, &pkt->data[3], pkt->len-3);
else {
fprintf(stderr, "EDOOFUS: NON-Lua PACKET *MUST NOT* REACH common.net_recv!\n");
fflush(stderr);
abort();
}
lua_pushnil(L);
return 2;
}
}