Files
metin2-client/tests/meshport/network_paths_test.cpp
T
shenandshen 507e687867 feat: add MeshPort overlay networking, item actions window, and AVA2 client enhancements
- add MeshPort peer-to-peer overlay networking SDK, C++ integration, and Python bindings
- add item actions quick use/split/delete/lock overlay window (mt_item_actions)
- add shop name range and NPC name visibility settings to system display options
- support battlepass ticket activation and month validation in mt_battlepass
- support boss tracking teleport destination button in mt_bosses
- support asynchronous account registration cancellation and pending login cancellation
- update AVA2 item conversion for attr lock scrolls and battlepass tickets
- update .gitignore for local build outputs and caches
2026-10-16 01:29:57 -07:00

67 lines
4.8 KiB
C++

// Exercise the real runtime and the SDK's real subnet gateway, without a login UI or OS routes.
#include "transport.h"
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <unistd.h>
#include <chrono>
#include <thread>
#include <fstream>
#include <iterator>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#define CHECK(x) do{if(!(x)){std::fprintf(stderr,"line %d: %s errno=%d\n",__LINE__,#x,errno);std::abort();}}while(0)
using namespace mt::meshport;
sockaddr_in endpoint(const char* host,int port){sockaddr_in result{};result.sin_family=AF_INET;result.sin_port=htons(port);CHECK(inet_pton(AF_INET,host,&result.sin_addr)==1);return result;}
int tcp(int port){
int fd=::socket(AF_INET,SOCK_STREAM,0);CHECK(fd>=0);fcntl(fd,F_SETFL,O_NONBLOCK);
CHECK(connect_socket(fd,endpoint("10.253.79.1",port))==-1);CHECK(pending(fd));
auto deadline=std::chrono::steady_clock::now()+std::chrono::seconds(70);int state=0;
while(!state&&std::chrono::steady_clock::now()<deadline){state=poll_socket(fd);std::this_thread::sleep_for(std::chrono::milliseconds(1));}
CHECK(state==1);fcntl(fd,F_SETFL,0);timeval timeout{3,0};setsockopt(fd,SOL_SOCKET,SO_RCVTIMEO,&timeout,sizeof(timeout));return fd;
}
void finish(int fd){forget_socket(fd);::close(fd);}
void echo(int port,const char* text){
int fd=tcp(port);size_t size=std::strlen(text);CHECK(::send(fd,text,size,0)==static_cast<ssize_t>(size));
char data[128]{};size_t got=0;while(got<size){auto count=::recv(fd,data+got,sizeof(data)-got,0);CHECK(count>0);got+=count;}
CHECK(got==size&&std::memcmp(data,text,size)==0);finish(fd);
}
int main(int argc,char** argv){
CHECK(argc==4);std::ifstream input(argv[1]);CHECK(input.good());std::string config((std::istreambuf_iterator<char>(input)),{});
// An invalid profile fails without direct fallback; a later request can retry with a valid profile.
configure("invalid saved profile");
int failed=::socket(AF_INET,SOCK_STREAM,0);CHECK(failed>=0);fcntl(failed,F_SETFL,O_NONBLOCK);
CHECK(connect_socket(failed,endpoint("10.253.79.1",std::atoi(argv[2])))==-1);
int failed_state=0;auto failed_deadline=std::chrono::steady_clock::now()+std::chrono::seconds(5);
while(!failed_state&&std::chrono::steady_clock::now()<failed_deadline){failed_state=poll_socket(failed);std::this_thread::sleep_for(std::chrono::milliseconds(1));}
CHECK(failed_state==-1);finish(failed);take_event();
configure(config);int tcp_port=std::atoi(argv[2]),udp_port=std::atoi(argv[3]);
int fd=::socket(AF_INET,SOCK_DGRAM,0);CHECK(fd>=0);fcntl(fd,F_SETFL,O_NONBLOCK);
auto local=endpoint("127.0.0.1",udp_port);auto remote=endpoint("10.253.79.1",udp_port);
CHECK(send_datagram(fd,"LAN",3,0,local)==3);
sockaddr_in source{};char data[128]{};ssize_t count=-1;
for(int i=0;i<3000&&count<0;++i){count=recv_datagram(fd,data,sizeof(data),0,&source);std::this_thread::sleep_for(std::chrono::milliseconds(1));}
CHECK(count==3&&std::memcmp(data,"LAN",3)==0&&source.sin_addr.s_addr==local.sin_addr.s_addr);
CHECK(take_event()==0); // Local traffic has not started the runtime.
CHECK(send_datagram(fd,"MESH",4,0,remote)==4); // First remote operation is UDP, not login.
auto deadline=std::chrono::steady_clock::now()+std::chrono::seconds(70);
while(!datagram_ready(fd)&&std::chrono::steady_clock::now()<deadline)std::this_thread::sleep_for(std::chrono::milliseconds(2));
CHECK(datagram_ready(fd));
CHECK(recv_datagram(fd,data,2,MSG_PEEK,&source)==2);CHECK(std::memcmp(data,"ME",2)==0);
CHECK(source.sin_addr.s_addr==remote.sin_addr.s_addr&&source.sin_port==remote.sin_port);
CHECK(recv_datagram(fd,data,sizeof(data),0,&source)==4);CHECK(std::memcmp(data,"MESH",4)==0);
CHECK(!datagram_ready(fd));
std::thread auth([&]{echo(tcp_port,"account-connection");});
std::thread game([&]{echo(tcp_port,"game-map-reconnect-guild-marks");});auth.join();game.join();
int http=tcp(tcp_port);const char request[]="POST /register HTTP/1.0\r\nContent-Length: 0\r\n\r\n";
CHECK(::send(http,request,sizeof(request)-1,0)>0);count=::recv(http,data,sizeof(data)-1,0);CHECK(count>0);
data[count]=0;CHECK(std::strstr(data,"200 OK")&&std::strstr(data,"OK\n"));finish(http);
// A new request outside every learned route stays pending; it must not connect directly.
int missing=::socket(AF_INET,SOCK_STREAM,0);CHECK(missing>=0);fcntl(missing,F_SETFL,O_NONBLOCK);
CHECK(connect_socket(missing,endpoint("10.252.79.1",tcp_port))==-1);CHECK(pending(missing));
CHECK(poll_socket(missing)==0);socklen_t len=sizeof(source);CHECK(getpeername(missing,reinterpret_cast<sockaddr*>(&source),&len)<0);
finish(missing);finish(fd);shutdown();
std::puts("Real SDK: LAN bypass, UDP-first DHCP, UDP source/peek, concurrent TCP, HTTP, reconnect and missing-route cancellation passed");
}