forked from Mirrorlandia_minetest/minetest
57a59ae92d
This is a follow-up change which disables class copies where possible to avoid unnecessary memory movements.
379 lines
9.4 KiB
C++
379 lines
9.4 KiB
C++
/*
|
|
Minetest
|
|
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License as published by
|
|
the Free Software Foundation; either version 2.1 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program 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 Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License along
|
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
#include "test.h"
|
|
|
|
#include "log.h"
|
|
#include "porting.h"
|
|
#include "settings.h"
|
|
#include "util/serialize.h"
|
|
#include "network/connection.h"
|
|
#include "network/networkpacket.h"
|
|
#include "network/socket.h"
|
|
|
|
class TestConnection : public TestBase {
|
|
public:
|
|
TestConnection()
|
|
{
|
|
if (INTERNET_SIMULATOR == false)
|
|
TestManager::registerTestModule(this);
|
|
}
|
|
|
|
const char *getName() { return "TestConnection"; }
|
|
|
|
void runTests(IGameDef *gamedef);
|
|
|
|
void testNetworkPacketSerialize();
|
|
void testHelpers();
|
|
void testConnectSendReceive();
|
|
};
|
|
|
|
static TestConnection g_test_instance;
|
|
|
|
void TestConnection::runTests(IGameDef *gamedef)
|
|
{
|
|
TEST(testNetworkPacketSerialize);
|
|
TEST(testHelpers);
|
|
TEST(testConnectSendReceive);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
struct Handler : public con::PeerHandler
|
|
{
|
|
Handler(const char *a_name) : name(a_name) {}
|
|
|
|
void peerAdded(con::Peer *peer)
|
|
{
|
|
infostream << "Handler(" << name << ")::peerAdded(): "
|
|
"id=" << peer->id << std::endl;
|
|
last_id = peer->id;
|
|
count++;
|
|
}
|
|
|
|
void deletingPeer(con::Peer *peer, bool timeout)
|
|
{
|
|
infostream << "Handler(" << name << ")::deletingPeer(): "
|
|
"id=" << peer->id << ", timeout=" << timeout << std::endl;
|
|
last_id = peer->id;
|
|
count--;
|
|
}
|
|
|
|
s32 count = 0;
|
|
u16 last_id = 0;
|
|
const char *name;
|
|
};
|
|
|
|
void TestConnection::testNetworkPacketSerialize()
|
|
{
|
|
const static u8 expected[] = {
|
|
0x00, 0x7b,
|
|
0x00, 0x02, 0xd8, 0x42, 0xdf, 0x9a
|
|
};
|
|
|
|
if (sizeof(wchar_t) == 2)
|
|
warningstream << __FUNCTION__ << " may fail on this platform." << std::endl;
|
|
|
|
{
|
|
NetworkPacket pkt(123, 0);
|
|
|
|
// serializing wide strings should do surrogate encoding, we test that here
|
|
pkt << std::wstring(L"\U00020b9a");
|
|
|
|
auto buf = pkt.oldForgePacket();
|
|
UASSERTEQ(int, buf.getSize(), sizeof(expected));
|
|
UASSERT(!memcmp(expected, &buf[0], buf.getSize()));
|
|
}
|
|
|
|
{
|
|
NetworkPacket pkt;
|
|
pkt.putRawPacket(expected, sizeof(expected), 0);
|
|
|
|
// same for decoding
|
|
std::wstring pkt_s;
|
|
pkt >> pkt_s;
|
|
|
|
UASSERT(pkt_s == L"\U00020b9a");
|
|
}
|
|
}
|
|
|
|
void TestConnection::testHelpers()
|
|
{
|
|
// Some constants for testing
|
|
u32 proto_id = 0x12345678;
|
|
session_t peer_id = 123;
|
|
u8 channel = 2;
|
|
SharedBuffer<u8> data1(1);
|
|
data1[0] = 100;
|
|
Address a(127,0,0,1, 10);
|
|
const u16 seqnum = 34352;
|
|
|
|
con::BufferedPacketPtr p1 = con::makePacket(a, data1,
|
|
proto_id, peer_id, channel);
|
|
/*
|
|
We should now have a packet with this data:
|
|
Header:
|
|
[0] u32 protocol_id
|
|
[4] session_t sender_peer_id
|
|
[6] u8 channel
|
|
Data:
|
|
[7] u8 data1[0]
|
|
*/
|
|
UASSERT(readU32(&p1->data[0]) == proto_id);
|
|
UASSERT(readU16(&p1->data[4]) == peer_id);
|
|
UASSERT(readU8(&p1->data[6]) == channel);
|
|
UASSERT(readU8(&p1->data[7]) == data1[0]);
|
|
|
|
//infostream<<"initial data1[0]="<<((u32)data1[0]&0xff)<<std::endl;
|
|
|
|
SharedBuffer<u8> p2 = con::makeReliablePacket(data1, seqnum);
|
|
|
|
/*infostream<<"p2.getSize()="<<p2.getSize()<<", data1.getSize()="
|
|
<<data1.getSize()<<std::endl;
|
|
infostream<<"readU8(&p2[3])="<<readU8(&p2[3])
|
|
<<" p2[3]="<<((u32)p2[3]&0xff)<<std::endl;
|
|
infostream<<"data1[0]="<<((u32)data1[0]&0xff)<<std::endl;*/
|
|
|
|
UASSERT(p2.getSize() == 3 + data1.getSize());
|
|
UASSERT(readU8(&p2[0]) == con::PACKET_TYPE_RELIABLE);
|
|
UASSERT(readU16(&p2[1]) == seqnum);
|
|
UASSERT(readU8(&p2[3]) == data1[0]);
|
|
}
|
|
|
|
|
|
void TestConnection::testConnectSendReceive()
|
|
{
|
|
/*
|
|
Test some real connections
|
|
|
|
NOTE: This mostly tests the legacy interface.
|
|
*/
|
|
|
|
u32 proto_id = 0xad26846a;
|
|
|
|
Handler hand_server("server");
|
|
Handler hand_client("client");
|
|
|
|
Address address(0, 0, 0, 0, 30001);
|
|
Address bind_addr(0, 0, 0, 0, 30001);
|
|
/*
|
|
* Try to use the bind_address for servers with no localhost address
|
|
* For example: FreeBSD jails
|
|
*/
|
|
std::string bind_str = g_settings->get("bind_address");
|
|
try {
|
|
bind_addr.Resolve(bind_str.c_str());
|
|
|
|
if (!bind_addr.isIPv6()) {
|
|
address = bind_addr;
|
|
}
|
|
} catch (ResolveError &e) {
|
|
}
|
|
|
|
infostream << "** Creating server Connection" << std::endl;
|
|
con::Connection server(proto_id, 512, 5.0, false, &hand_server);
|
|
server.Serve(address);
|
|
|
|
infostream << "** Creating client Connection" << std::endl;
|
|
con::Connection client(proto_id, 512, 5.0, false, &hand_client);
|
|
|
|
UASSERT(hand_server.count == 0);
|
|
UASSERT(hand_client.count == 0);
|
|
|
|
sleep_ms(50);
|
|
|
|
Address server_address(127, 0, 0, 1, 30001);
|
|
if (address != Address(0, 0, 0, 0, 30001)) {
|
|
server_address = bind_addr;
|
|
}
|
|
|
|
infostream << "** running client.Connect()" << std::endl;
|
|
client.Connect(server_address);
|
|
|
|
sleep_ms(50);
|
|
|
|
// Client should not have added client yet
|
|
UASSERT(hand_client.count == 0);
|
|
|
|
try {
|
|
NetworkPacket pkt;
|
|
infostream << "** running client.Receive()" << std::endl;
|
|
client.Receive(&pkt);
|
|
infostream << "** Client received: peer_id=" << pkt.getPeerId()
|
|
<< ", size=" << pkt.getSize() << std::endl;
|
|
} catch (con::NoIncomingDataException &e) {
|
|
}
|
|
|
|
// Client should have added server now
|
|
UASSERT(hand_client.count == 1);
|
|
UASSERT(hand_client.last_id == 1);
|
|
// Server should not have added client yet
|
|
UASSERT(hand_server.count == 0);
|
|
|
|
sleep_ms(100);
|
|
|
|
try {
|
|
NetworkPacket pkt;
|
|
infostream << "** running server.Receive()" << std::endl;
|
|
server.Receive(&pkt);
|
|
infostream << "** Server received: peer_id=" << pkt.getPeerId()
|
|
<< ", size=" << pkt.getSize()
|
|
<< std::endl;
|
|
} catch (con::NoIncomingDataException &e) {
|
|
// No actual data received, but the client has
|
|
// probably been connected
|
|
}
|
|
|
|
// Client should be the same
|
|
UASSERT(hand_client.count == 1);
|
|
UASSERT(hand_client.last_id == 1);
|
|
// Server should have the client
|
|
UASSERT(hand_server.count == 1);
|
|
UASSERT(hand_server.last_id == 2);
|
|
|
|
//sleep_ms(50);
|
|
|
|
while (client.Connected() == false) {
|
|
try {
|
|
NetworkPacket pkt;
|
|
infostream << "** running client.Receive()" << std::endl;
|
|
client.Receive(&pkt);
|
|
infostream << "** Client received: peer_id=" << pkt.getPeerId()
|
|
<< ", size=" << pkt.getSize() << std::endl;
|
|
} catch (con::NoIncomingDataException &e) {
|
|
}
|
|
sleep_ms(50);
|
|
}
|
|
|
|
sleep_ms(50);
|
|
|
|
try {
|
|
NetworkPacket pkt;
|
|
infostream << "** running server.Receive()" << std::endl;
|
|
server.Receive(&pkt);
|
|
infostream << "** Server received: peer_id=" << pkt.getPeerId()
|
|
<< ", size=" << pkt.getSize()
|
|
<< std::endl;
|
|
} catch (con::NoIncomingDataException &e) {
|
|
}
|
|
|
|
/*
|
|
Simple send-receive test
|
|
*/
|
|
{
|
|
NetworkPacket pkt;
|
|
pkt.putRawPacket((u8*) "Hello World !", 14, 0);
|
|
|
|
auto sentdata = pkt.oldForgePacket();
|
|
|
|
infostream<<"** running client.Send()"<<std::endl;
|
|
client.Send(PEER_ID_SERVER, 0, &pkt, true);
|
|
|
|
sleep_ms(50);
|
|
|
|
NetworkPacket recvpacket;
|
|
infostream << "** running server.Receive()" << std::endl;
|
|
server.Receive(&recvpacket);
|
|
infostream << "** Server received: peer_id=" << pkt.getPeerId()
|
|
<< ", size=" << pkt.getSize()
|
|
<< ", data=" << (const char*)pkt.getU8Ptr(0)
|
|
<< std::endl;
|
|
|
|
auto recvdata = pkt.oldForgePacket();
|
|
|
|
UASSERT(memcmp(*sentdata, *recvdata, recvdata.getSize()) == 0);
|
|
}
|
|
|
|
session_t peer_id_client = 2;
|
|
/*
|
|
Send a large packet
|
|
*/
|
|
{
|
|
const int datasize = 30000;
|
|
NetworkPacket pkt(0, datasize);
|
|
for (u16 i=0; i<datasize; i++) {
|
|
pkt << (u8) i/4;
|
|
}
|
|
|
|
infostream << "Sending data (size=" << datasize << "):";
|
|
for (int i = 0; i < datasize && i < 20; i++) {
|
|
if (i % 2 == 0)
|
|
infostream << " ";
|
|
char buf[10];
|
|
porting::mt_snprintf(buf, sizeof(buf), "%.2X",
|
|
((int)((const char *)pkt.getU8Ptr(0))[i]) & 0xff);
|
|
infostream<<buf;
|
|
}
|
|
if (datasize > 20)
|
|
infostream << "...";
|
|
infostream << std::endl;
|
|
|
|
auto sentdata = pkt.oldForgePacket();
|
|
|
|
server.Send(peer_id_client, 0, &pkt, true);
|
|
|
|
//sleep_ms(3000);
|
|
|
|
Buffer<u8> recvdata;
|
|
infostream << "** running client.Receive()" << std::endl;
|
|
session_t peer_id = 132;
|
|
u16 size = 0;
|
|
bool received = false;
|
|
u64 timems0 = porting::getTimeMs();
|
|
for (;;) {
|
|
if (porting::getTimeMs() - timems0 > 5000 || received)
|
|
break;
|
|
try {
|
|
NetworkPacket pkt;
|
|
client.Receive(&pkt);
|
|
size = pkt.getSize();
|
|
peer_id = pkt.getPeerId();
|
|
recvdata = pkt.oldForgePacket();
|
|
received = true;
|
|
} catch (con::NoIncomingDataException &e) {
|
|
}
|
|
sleep_ms(10);
|
|
}
|
|
UASSERT(received);
|
|
infostream << "** Client received: peer_id=" << peer_id
|
|
<< ", size=" << size << std::endl;
|
|
|
|
infostream << "Received data (size=" << size << "): ";
|
|
for (int i = 0; i < size && i < 20; i++) {
|
|
if (i % 2 == 0)
|
|
infostream << " ";
|
|
char buf[10];
|
|
porting::mt_snprintf(buf, sizeof(buf), "%.2X", ((int)(recvdata[i])) & 0xff);
|
|
infostream << buf;
|
|
}
|
|
if (size > 20)
|
|
infostream << "...";
|
|
infostream << std::endl;
|
|
|
|
UASSERT(memcmp(*sentdata, *recvdata, recvdata.getSize()) == 0);
|
|
UASSERT(peer_id == PEER_ID_SERVER);
|
|
}
|
|
|
|
// Check peer handlers
|
|
UASSERT(hand_client.count == 1);
|
|
UASSERT(hand_client.last_id == 1);
|
|
UASSERT(hand_server.count == 1);
|
|
UASSERT(hand_server.last_id == 2);
|
|
}
|