diff --git a/src/k4.h b/src/k4.h index 38e5d96..3e36422 100644 --- a/src/k4.h +++ b/src/k4.h @@ -5,20 +5,26 @@ struct k3MScreen; #include"stoon.h" -struct NetWrap { +/*struct NetWrap { struct Stoon stoon; int stage; float timeout; - int isHost; + bool isHost; // Used for punching stage int gotten; - char otherpeer[STOON_CONN_INFO_SIZE]; + // Server-side +#define NETWRAP_BACKLOG 32 + char otherpeers[NETWRAP_BACKLOG][STOON_CONN_INFO_SIZE]; + bool otherpeersActive[NETWRAP_BACKLOG]; + + // Client-side + char connectto[STOON_CONN_INFO_SIZE]; }; -extern struct NetWrap NetWrap; +extern struct NetWrap NetWrap;*/ extern struct k3MScreen *UiActive; diff --git a/src/luaapi.c b/src/luaapi.c index aac4934..ada5ff5 100644 --- a/src/luaapi.c +++ b/src/luaapi.c @@ -14,6 +14,7 @@ #include"net.h" #include"k3menu.h" #include +#include"net_hi.h" /* * This is by far the least clean or well-maintained source in the @@ -2131,18 +2132,42 @@ static int dagame_set_texture_reduction(lua_State *L) { } int PeercodeHandler = LUA_NOREF; -static int dagame_net_gen_peercode(lua_State *L) { - if(NetWrap.stage != 0 || PeercodeHandler != LUA_NOREF) { - lua_pushliteral(L, "Peercode generation already in progress"); - lua_error(L); - return 0; +static void get_peercode_callback(void *ud, const char *peercode_bin) { + char peercode[STOON_CONN_INFO_SIZE * 2 + 1] = {}; + for(int i = 0; i < STOON_CONN_INFO_SIZE; i++) { + snprintf(peercode + i * 2, 3, "%02x", ((uint8_t*) peercode_bin)[i]); } - NetWrap.stoon = stoon_init("stun.easybell.de", 3478, 26656); - NetWrap.stage = 1; + if(PeercodeHandler == LUA_NOREF) { + puts("Peercode found but no handler"); + return; + } - lua_pushvalue(L, 1); - PeercodeHandler = luaL_ref(L, LUA_REGISTRYINDEX); + lua_rawgeti(L, LUA_REGISTRYINDEX, PeercodeHandler); + if(lua_isnil(L, -1)) { + lua_pop(L, 1); + return; + } else { + lua_pushstring(L, peercode); + if(lua_pcall(L, 1, 0, 0) != LUA_OK) { + puts(lua_tostring(L, -1)); + lua_pop(L, 1); + return; + } + } + lua_pop(L, 1); + + luaL_unref(L, LUA_REGISTRYINDEX, PeercodeHandler); + PeercodeHandler = LUA_NOREF; +} +static int dagame_net_gen_peercode(lua_State *L) { + if(net_hi_request_peercode(NULL, get_peercode_callback)) { + lua_pushvalue(L, 1); + PeercodeHandler = luaL_ref(L, LUA_REGISTRYINDEX); + } else { + lua_pushliteral(L, "Cannot request peercode"); + lua_error(L); + } return 0; } @@ -2175,8 +2200,14 @@ static int dagame_clipboard_get(lua_State *L) { return 0; } -#define CONN_INVALID_PEERCODE 1 -static int conn(const char *peercode, bool host) { +static int get_peercode_bin(const char *peercode, char *peercode_bin) { + if(!peercode) return 0; + if(!peercode_bin) return 0; + + if(strlen(peercode) < 2 * STOON_CONN_INFO_SIZE) { + return 0; + } + for(int i = 0; i < STOON_CONN_INFO_SIZE; i++) { int b = 0; @@ -2184,55 +2215,52 @@ static int conn(const char *peercode, bool host) { b |= (peercode[i * 2 + 0] - '0') << 4; } else if(peercode[i * 2 + 0] >= 'a' && peercode[i * 2 + 0] <= 'f') { b |= (peercode[i * 2 + 0] - 'a' + 10) << 4; - } else return CONN_INVALID_PEERCODE; + } else return 0; if(peercode[i * 2 + 1] >= '0' && peercode[i * 2 + 1] <= '9') { b |= (peercode[i * 2 + 1] - '0') << 0; } else if(peercode[i * 2 + 1] >= 'a' && peercode[i * 2 + 1] <= 'f') { b |= (peercode[i * 2 + 1] - 'a' + 10) << 0; - } else return CONN_INVALID_PEERCODE; + } else return 0; - NetWrap.otherpeer[i] = b; + peercode_bin[i] = b; } - NetWrap.isHost = host; - NetWrap.stage = 3; + return 1; +} + +static int dagame_net_host(lua_State *L) { + lua_pushboolean(L, net_hi_setup(true)); return 0; } -static int dagame_net_host(lua_State *L) { - int status = conn(lua_tostring(L, 1), true); - - lua_pushboolean(L, !status); - - if(status) { - if(status == CONN_INVALID_PEERCODE) { - lua_pushliteral(L, "peercode"); - } else { - lua_pushliteral(L, "unknown"); - } - return 2; +static int dagame_net_join(lua_State *L) { + char peercode[STOON_CONN_INFO_SIZE]; + if(!get_peercode_bin(lua_tostring(L, 1), peercode)) { + lua_pushliteral(L, "Invalid peercode"); + lua_error(L); + return 0; } + net_hi_setup(false); + + lua_pushboolean(L, net_hi_connect(peercode)); + return 1; } -static int dagame_net_join(lua_State *L) { - int status = conn(lua_tostring(L, 1), false); - - lua_pushboolean(L, !status); - - if(status) { - if(status == CONN_INVALID_PEERCODE) { - lua_pushliteral(L, "peercode"); - } else { - lua_pushliteral(L, "unknown"); - } - return 2; +static int dagame_net_punch(lua_State *L) { + char peercode[STOON_CONN_INFO_SIZE]; + if(!get_peercode_bin(lua_tostring(L, 1), peercode)) { + lua_pushliteral(L, "Invalid peercode"); + lua_error(L); + return 0; } - return 1; + net_hi_add_punch(peercode); + + return 0; } #include @@ -2403,6 +2431,9 @@ void luaapi_init() { lua_pushcfunction(L, dagame_net_host); lua_setfield(L, -2, "host"); + lua_pushcfunction(L, dagame_net_punch); + lua_setfield(L, -2, "punch"); + lua_pushcfunction(L, dagame_net_join); lua_setfield(L, -2, "join"); lua_setfield(L, -2, "net"); @@ -3337,22 +3368,6 @@ void luaapi_cleanup() { lua_pop(L, 1); } -/*void luaapi_perform(const char *statement) { - puts(statement); - if(luaL_loadstring(L, statement) != LUA_OK) { - puts(lua_tostring(L, -1)); - lua_pop(L, 1); - return; - } - lua_rawgeti(L, LUA_REGISTRYINDEX, consoleEnvironment); - lua_setupvalue(L, -2, 1); - if(lua_pcall(L, 0, 0, 0) != LUA_OK) { - puts(lua_tostring(L, -1)); - lua_pop(L, 1); - return; - } -}*/ - void luaapi_ctrlev(int ctrl, int action) { lua_getglobal(L, "game"); lua_getfield(L, -1, "ctrl"); @@ -3430,30 +3445,6 @@ void luaapi_escape() { lua_pop(L, 1); } -void luaapi_peercode_found(const char *peercode) { - if(PeercodeHandler == LUA_NOREF) { - puts("Peercode found but no handler"); - return; - } - - lua_rawgeti(L, LUA_REGISTRYINDEX, PeercodeHandler); - if(lua_isnil(L, -1)) { - lua_pop(L, 1); - return; - } else { - lua_pushstring(L, peercode); - if(lua_pcall(L, 1, 0, 0) != LUA_OK) { - puts(lua_tostring(L, -1)); - lua_pop(L, 1); - return; - } - } - lua_pop(L, 1); - - luaL_unref(L, LUA_REGISTRYINDEX, PeercodeHandler); - PeercodeHandler = LUA_NOREF; -} - void luaapi_update() { lua_getglobal(L, "game"); lua_getfield(L, -1, "update"); diff --git a/src/main.c b/src/main.c index f392bdf..799098c 100644 --- a/src/main.c +++ b/src/main.c @@ -40,8 +40,6 @@ #include -struct NetWrap NetWrap; - GLFWwindow *GameWnd; static int TextureResolutionReduction = 0; @@ -186,7 +184,7 @@ const char *k4_get_arg(const char *name) { return NULL; } -static void netwrap_step() { +/*static void netwrap_step() { if(NetWrap.stage && CurrentTime >= NetWrap.timeout) { if(NetWrap.stage == 1) { if(stoon_req(&NetWrap.stoon)) { @@ -201,11 +199,6 @@ static void netwrap_step() { luaapi_peercode_found(str); - //glfwSetClipboardString(NULL, str); - - //screenMain.lblpeerdata->invisible = 0; - //screenMain.btnconnect->invisible = 0; - NetWrap.stage = 2; } else { k3Log(k3_INFO, "Stoon listen timeout."); @@ -217,51 +210,59 @@ static void netwrap_step() { } } else if(NetWrap.stage == 2) { stoon_keepalive(&NetWrap.stoon); - NetWrap.timeout = CurrentTime + 1; } else if(NetWrap.stage == 3) { - int status = stoon_poonch(&NetWrap.stoon, NetWrap.otherpeer); - - //screenMain.lblpeerdata->txt = strdup("Trying to connect...\nMay not work if both are\nbehind symmetric NATs."); - - if(status == STOON_POONCH_INCOMPATIBLE_NETFAMS) { - NetWrap.stage = 0; - + if(NetWrap.isHost) { stoon_kill(&NetWrap.stoon); + net_server_init(); + NetWrap.stage = 4; + } + } else if(NetWrap.stage == 4) { + if(NetWrap.isHost) { + ENetBuffer buf = {.data = "Punch!", .dataLength = 6}; - //screenMain.lblpeerdata->txt = strdup("Connection cannot be established:\nIncompatible netfams."); - } else if(status == STOON_POONCH_NO_KNOCK && NetWrap.stoon.poonchstage == POONCH_STAGE_ACK) { - if(NetWrap.gotten++ > 5) { - NetWrap.stage = 0; - stoon_kill(&NetWrap.stoon); - - if(NetWrap.isHost) { - net_server_init(); - } else { - char ip[64]; - int port; - if(*(uint16_t*) (NetWrap.otherpeer + 22) == 0) { - inet_ntop(AF_INET, NetWrap.otherpeer, ip, sizeof(ip)); - port = ntohs(*(uint16_t*) (NetWrap.otherpeer + 4)); - } else { - inet_ntop(AF_INET6, NetWrap.otherpeer + 6, ip, sizeof(ip)); - port = ntohs(*(uint16_t*) (NetWrap.otherpeer + 22)); - } + ENetHost *h = net_server_get_enethost(); + + for(size_t i = 0; i < NETWRAP_BACKLOG; i++) { + if(NetWrap.otherpeersActive[i]) { + ENetAddress v4 = {}, v6 = {}; + stoonpeer_to_enets(NetWrap.otherpeers[i], &v4, &v6); - printf("Trying [%s]:%u\n", ip, port); - - net_client_init(); - net_client_connect(ip, port); + enet_socket_send(h->socket, &v4, &buf, 1); + enet_socket_send(h->socket, &v6, &buf, 1); } - - //screenMain.lblpeerdata->txt = strdup("Connection successful."); } - } else NetWrap.gotten = 0; + } else { + stoon_keepalive(&NetWrap.stoon); + } + NetWrap.timeout = CurrentTime + 1; + } else if(NetWrap.stage == 5) { + if(!NetWrap.isHost) { + char ip[64]; + int port; + if(*(uint16_t*) (NetWrap.otherpeer + 22) == 0) { + inet_ntop(AF_INET, NetWrap.otherpeer, ip, sizeof(ip)); + port = ntohs(*(uint16_t*) (NetWrap.otherpeer + 4)); + } else { + inet_ntop(AF_INET6, NetWrap.otherpeer + 6, ip, sizeof(ip)); + port = ntohs(*(uint16_t*) (NetWrap.otherpeer + 22)); + } + + printf("Trying [%s]:%u\n", ip, port); + + net_client_init(); + if(net_client_connect(ip, port)) { + NetWrap.stage = 5; + } + } + + NetWrap.timeout = CurrentTime + 0.5; + } else { NetWrap.timeout = CurrentTime + 1; } } -} +}*/ #include void k4k3LogCallback(enum k3LogLevel lvl, const char *str, size_t len) { @@ -373,150 +374,6 @@ static void fix_resol() { gr_lowres(roundf(w * ResolPercentage / 100.f / 4) * 4, roundf(h * ResolPercentage / 100.f / 4) * 4); } -static bool onqualitypress(struct k3MEvent *ev, uint8_t *ud) { - k3GraphicalReduction = (k3GraphicalReduction + 1) % 3; - return true; -} -static bool onresolpress(struct k3MEvent *ev, uint8_t *ud) { - struct k3MTextButton *btn = (void*) ev->target; - - ResolPercentage -= 10; - - if(ResolPercentage <= 0) { - ResolPercentage = 100; - } - - const char *txt = NULL; - - switch(ResolPercentage) { - case 10: - txt = "10%"; - break; - case 20: - txt = "20%"; - break; - case 30: - txt = "30%"; - break; - case 40: - txt = "40%"; - break; - case 50: - txt = "50%"; - break; - case 60: - txt = "60%"; - break; - case 70: - txt = "70%"; - break; - case 80: - txt = "80%"; - break; - case 90: - txt = "90%"; - break; - case 100: - txt = "Full Resolution"; - break; - } - - btn->txt = strdup(txt); - - fix_resol(); - - return true; -} -static bool ontexrespress(struct k3MEvent *ev, uint8_t *ud) { - TextureResolutionReduction = (TextureResolutionReduction + 1) % 3; - refresh_textures(); - - return true; -} -static bool onresumepress(struct k3MEvent *ev, uint8_t *ud) { - UiActive = NULL; - set_ui_mode(0); - - return true; -} -#ifdef LOCALHOST_ONLY -static bool onhostpress(struct k3MEvent *ev, uint8_t *ud) { - net_server_init(); - return true; -} -static bool onjoinpress(struct k3MEvent *ev, uint8_t *ud) { - net_client_init(); - net_client_connect("127.0.0.1", 26656); - return true; -} -static bool onconnectpress(struct k3MEvent *ev, uint8_t *ud) { - return true; -} -#else -static bool onhostpress(struct k3MEvent *ev, uint8_t *ud) { - screenMain.btnhost->disabled = 1; - screenMain.btnjoin->disabled = 1; - - NetWrap.stage = 1; - NetWrap.timeout = glfwGetTime() + 0; - NetWrap.isHost = 1; - NetWrap.stoon = stoon_init("stun.easybell.de", 3478, 26656); - - return true; -} -static bool onjoinpress(struct k3MEvent *ev, uint8_t *ud) { - screenMain.btnhost->disabled = 1; - screenMain.btnjoin->disabled = 1; - - NetWrap.stage = 1; - NetWrap.timeout = glfwGetTime() + 0; - NetWrap.isHost = 0; - NetWrap.stoon = stoon_init("stun.easybell.de", 3478, 26656); - - return true; -} -static bool onconnectpress(struct k3MEvent *ev, uint8_t *ud) { - const char *name = glfwGetClipboardString(NULL); - - while(isspace(*name)) name++; - - if(strlen(name) < STOON_CONN_INFO_SIZE * 2) { - goto bad; - } - - for(int i = 0; i < STOON_CONN_INFO_SIZE; i++) { - int b = 0; - - if(name[i * 2 + 0] >= '0' && name[i * 2 + 0] <= '9') { - b |= (name[i * 2 + 0] - '0') << 4; - } else if(name[i * 2 + 0] >= 'a' && name[i * 2 + 0] <= 'f') { - b |= (name[i * 2 + 0] - 'a' + 10) << 4; - } else goto bad; - - if(name[i * 2 + 1] >= '0' && name[i * 2 + 1] <= '9') { - b |= (name[i * 2 + 1] - '0') << 0; - } else if(name[i * 2 + 1] >= 'a' && name[i * 2 + 1] <= 'f') { - b |= (name[i * 2 + 1] - 'a' + 10) << 0; - } else goto bad; - - NetWrap.otherpeer[i] = b; - } - - NetWrap.stage = 3; - screenMain.btnconnect->disabled = 1; - - return true; - -bad: - screenMain.lblpeerdata->txt = strdup("Incorrect peer conndata."); - - return true; -} -#endif -static bool onconsoleenter(struct k3MEvent *ev, uint8_t *ud) { - return true; -} - static void eng_ui_init() { set_ui_mode(1); } @@ -663,7 +520,7 @@ int main(int argc_, char **argv_) { glfwPollEvents(); - netwrap_step(); + net_hi_update(CurrentTime); float alpha = fmodf(accumulator * GAME_TPS, 1.f); diff --git a/src/net_client.c b/src/net_client.c index 0417165..fe04bd3 100644 --- a/src/net_client.c +++ b/src/net_client.c @@ -26,12 +26,20 @@ void net_client_init() { Game.isAuthority = 0; } -void net_client_connect(const char *addr, uint16_t port) { +bool net_client_connect(const char *addr, uint16_t port) { ENetAddress eaddr; enet_address_set_host(&eaddr, addr); eaddr.port = port; peer = enet_host_connect(host, &eaddr, 1, 0); + + ENetEvent ev; + if(enet_host_service(host, &ev, 200) <= 0 || ev.type != ENET_EVENT_TYPE_CONNECT) { + enet_peer_reset(peer); + return false; + } + + return true; } extern double glfwGetTime(); diff --git a/src/net_client.h b/src/net_client.h index 1c81f90..fd303d7 100644 --- a/src/net_client.h +++ b/src/net_client.h @@ -1,7 +1,7 @@ #pragma once void net_client_init(); -void net_client_connect(const char *addr, uint16_t port); +bool net_client_connect(const char *addr, uint16_t port); void net_client_receive(); void net_client_update(); void net_client_dejitter(); diff --git a/src/net_hi.c b/src/net_hi.c new file mode 100644 index 0000000..441ca29 --- /dev/null +++ b/src/net_hi.c @@ -0,0 +1,165 @@ +#include"net_hi.h" + +#include +#include"enet.h" +#include +#include"stoon.h" +#include"net_server.h" +#include"net_client.h" +#include + +static void *ReqPeercodeUD; +static void(*ReqPeercodeCB)(void*, const char *peercode); + +static struct Stoon stoon; + +static bool inited = false; +static bool isHost; + +typedef struct { +#define MASK_IPv4 1 +#define MASK_IPv6 2 + int mask; + ENetAddress v4; + ENetAddress v6; +} ToPunch; +static ToPunch *topunch = NULL; +static size_t topunchCount = 0; + +static double Timeout = 0; + +static bool connected = false; + +bool net_hi_request_peercode(void *ud, void(*callback)(void*, const char *peercode)) { + if(inited) return false; + + ReqPeercodeUD = ud; + ReqPeercodeCB = callback; + + stoon = stoon_init("stun.easybell.de", 3478, 26656); + + Timeout = 0; + + return true; +} + +bool net_hi_setup(bool host) { + if(inited) return false; + + stoon_kill(&stoon); + + inited = true; + isHost = host; + + if(host) { + net_server_init(); + } else { + net_client_init(); + } + + return true; +} + +void net_hi_update(double now) { + if(now <= Timeout) return; + + if(ReqPeercodeCB) { + + if(stoon_req(&stoon)) { + if(stoon_listen(&stoon)) { + uint8_t peercode[STOON_CONN_INFO_SIZE] = {}; + stoon_serialize(&stoon, peercode); + + ReqPeercodeCB(ReqPeercodeUD, peercode); + + ReqPeercodeCB = NULL; + } else { + Timeout = now + 1; + } + } else { + Timeout = now + 1; + } + + } else if(!inited) { + + stoon_keepalive(&stoon); + + Timeout = now + 1; + + } else { + + if(isHost) { + + ENetHost *h = net_server_get_enethost(); + + ENetBuffer buf = {.data = "Punch!", .dataLength = 6}; + + for(size_t tpi = 0; tpi < topunchCount; tpi++) { + ToPunch *tp = &topunch[tpi]; + + if(tp->mask & MASK_IPv4) { + enet_socket_send(h->socket, &tp->v4, &buf, 1); + } else if(tp->mask & MASK_IPv6) { + enet_socket_send(h->socket, &tp->v6, &buf, 1); + } + } + + } + + Timeout = now + 1; + + } +} + +static void stoonpeer_to_enets(const char *peercode, ENetAddress *v4, ENetAddress *v6, int *mask) { + memset(v4->host.__in6_u.__u6_addr8, 0, 16); + v4->host.__in6_u.__u6_addr8[10] = 0xFF; + v4->host.__in6_u.__u6_addr8[11] = 0xFF; + v4->host.__in6_u.__u6_addr8[12] = peercode[0]; + v4->host.__in6_u.__u6_addr8[13] = peercode[1]; + v4->host.__in6_u.__u6_addr8[14] = peercode[2]; + v4->host.__in6_u.__u6_addr8[15] = peercode[3]; + v4->port = ntohs(*(uint16_t*) &peercode[4]); + + memcpy(v6->host.__in6_u.__u6_addr8, peercode + 6, 16); + v6->port = ntohs(*(uint16_t*) &peercode[22]); + + *mask = 0; + if(v4->port) { + *mask |= MASK_IPv4; + } + if(v6->port) { + *mask |= MASK_IPv6; + } +} + +void net_hi_add_punch(const char *peercode) { + int mask; + ENetAddress v4 = {}, v6 = {}; + stoonpeer_to_enets(peercode, &v4, &v6, &mask); + + topunch = realloc(topunch, sizeof(*topunch) * (++topunchCount)); + topunch[topunchCount - 1] = (ToPunch) { + .v4 = v4, + .v6 = v6, + .mask = mask, + }; +} + +bool net_hi_connect(const char *peercode) { + if(!inited) return false; + + if(connected) return false; + + char ip[64]; + int port; + if(*(uint16_t*) (peercode + 22) == 0) { + inet_ntop(AF_INET, peercode, ip, sizeof(ip)); + port = ntohs(*(uint16_t*) (peercode + 4)); + } else { + inet_ntop(AF_INET6, peercode + 6, ip, sizeof(ip)); + port = ntohs(*(uint16_t*) (peercode + 22)); + } + + return net_client_connect(ip, port); +} diff --git a/src/net_hi.h b/src/net_hi.h new file mode 100644 index 0000000..4e75804 --- /dev/null +++ b/src/net_hi.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +bool net_hi_request_peercode(void *ud, void(*callback)(void*, const char *peercode)); +bool net_hi_setup(bool host); +void net_hi_update(double now); + +void net_hi_add_punch(const char *peercode); + +bool net_hi_connect(const char *peercode); diff --git a/src/net_server.c b/src/net_server.c index b0c01d3..223f088 100644 --- a/src/net_server.c +++ b/src/net_server.c @@ -342,3 +342,7 @@ void net_server_broadcast_msg(struct bstr *data) { free(b.data); } + +void *net_server_get_enethost() { + return host; +} diff --git a/src/net_server.h b/src/net_server.h index a8b875c..9ffb8b5 100644 --- a/src/net_server.h +++ b/src/net_server.h @@ -14,3 +14,5 @@ void net_server_force_load(struct _ENetPeer *peer, const char *script); void net_server_send_msg(struct _ENetPeer *peer, struct bstr *data); void net_server_broadcast_msg(struct bstr *data); + +void *net_server_get_enethost();