- 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
67 lines
4.8 KiB
C++
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");
|
|
}
|