mirror of
https://github.com/therealKyp/Earth-and-Beyond-server
synced 2026-08-13 22:23:03 -04:00
448 lines
12 KiB
C++
448 lines
12 KiB
C++
|
|
#include "Net7.h"
|
||
|
|
#include "UDPClient.h"
|
||
|
|
#include "opcodes.h"
|
||
|
|
#include "PacketStructures.h"
|
||
|
|
#include "ServerManager.h"
|
||
|
|
|
||
|
|
void __cdecl LaunchUDPCRecvThread(void *arg)
|
||
|
|
{
|
||
|
|
((UDPClient *) arg)->RecvThread();
|
||
|
|
_endthread();
|
||
|
|
}
|
||
|
|
|
||
|
|
void __cdecl LaunchUDPMVASThread(void *arg)
|
||
|
|
{
|
||
|
|
((UDPClient *) arg)->MVASThread();
|
||
|
|
_endthread();
|
||
|
|
}
|
||
|
|
|
||
|
|
bool UDPClient::OpenFixedPort(short port, long ip_addr)
|
||
|
|
{
|
||
|
|
bool success = true;
|
||
|
|
ULONG uAddr = 0;
|
||
|
|
char strLocal[MAX_PATH] = { 0 };
|
||
|
|
|
||
|
|
m_Listen_Socket = socket(AF_INET, SOCK_DGRAM, 0);
|
||
|
|
|
||
|
|
if(m_Listen_Socket == INVALID_SOCKET)
|
||
|
|
{
|
||
|
|
LogMessage("Invalid Socket %d. Program Aborted\n", GetLastError());
|
||
|
|
success = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (SOCKET_ERROR != gethostname(strLocal, MAX_PATH))
|
||
|
|
{
|
||
|
|
struct hostent* hp;
|
||
|
|
hp = gethostbyname(strLocal);
|
||
|
|
if (hp != NULL)
|
||
|
|
{
|
||
|
|
strcpy(strLocal, hp->h_name);
|
||
|
|
uAddr = *((ULONG *) hp->h_addr_list[0]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
CreateFrom(uAddr,0);
|
||
|
|
|
||
|
|
if ( SOCKET_ERROR == bind(m_Listen_Socket, (sockaddr *) &m_SockAddr, sizeof(m_SockAddr)))
|
||
|
|
{
|
||
|
|
LogMessage("Socket Bind error '%d'. Connection not established\n", GetLastError());
|
||
|
|
closesocket( m_Listen_Socket );
|
||
|
|
success = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
CreateFrom(ip_addr, port);
|
||
|
|
|
||
|
|
if (SOCKET_ERROR == connect( m_Listen_Socket, (sockaddr *) &m_SockAddr, sizeof(m_SockAddr)))
|
||
|
|
{
|
||
|
|
closesocket( m_Listen_Socket );
|
||
|
|
success = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
return success;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool UDPClient::OpenMultiPort(short port, long ip_addr)
|
||
|
|
{
|
||
|
|
bool success = true;
|
||
|
|
|
||
|
|
m_Listen_Socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||
|
|
|
||
|
|
if(m_Listen_Socket == INVALID_SOCKET)
|
||
|
|
{
|
||
|
|
LogMessage("Invalid Socket %d. Program Aborted\n", GetLastError());
|
||
|
|
success = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
SetBroadcast(m_Listen_Socket);
|
||
|
|
|
||
|
|
return success;
|
||
|
|
}
|
||
|
|
|
||
|
|
UDPClient::UDPClient(short port, short connection_type, long ip_addr)
|
||
|
|
{
|
||
|
|
m_IPAddr = ip_addr;
|
||
|
|
m_Port = port;
|
||
|
|
m_ConnectionType = connection_type;
|
||
|
|
m_logged_in = false;
|
||
|
|
m_global_account_rcv = false;
|
||
|
|
m_PlayerID = 0;
|
||
|
|
memset(m_AccountName, 0, 128);
|
||
|
|
m_ClientPort = 0;
|
||
|
|
m_ClientIP = 0;
|
||
|
|
m_ConnectionActive = false;
|
||
|
|
m_AlternatePorts = false;
|
||
|
|
m_ServerTCP = (0);
|
||
|
|
m_CurrentPacketNum = 0;
|
||
|
|
bool success = false;
|
||
|
|
m_Resync = true;
|
||
|
|
m_PreferTCP = false;
|
||
|
|
m_UsingTCP = false;
|
||
|
|
m_PacketTimeout = 0;
|
||
|
|
m_PacketDropThisSession = 0;
|
||
|
|
m_Packets.clear();
|
||
|
|
|
||
|
|
memset(&m_Server_handoff, 0, sizeof(m_Server_handoff));
|
||
|
|
m_PacketResendTimer = 0;
|
||
|
|
|
||
|
|
//if we want a UDP port that can be used to send messages to different ports, it needs to be opened in a different way
|
||
|
|
switch (connection_type)
|
||
|
|
{
|
||
|
|
case CLIENT_TYPE_FIXED_PORT:
|
||
|
|
success = OpenFixedPort(port, ip_addr);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case CLIENT_TYPE_MULTI_PORT:
|
||
|
|
success = OpenMultiPort(port, ip_addr);
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
LogMessage("Bad port type.\n");
|
||
|
|
return;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!success)
|
||
|
|
{
|
||
|
|
LogMessage("Error while opening UDP port\n.");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
_beginthread(&LaunchUDPCRecvThread, 0, this);
|
||
|
|
|
||
|
|
if (connection_type == CLIENT_TYPE_FIXED_PORT)
|
||
|
|
{
|
||
|
|
_beginthread(&LaunchUDPMVASThread, 0, this);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
UDPClient::~UDPClient()
|
||
|
|
{
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
void UDPClient::CreateFrom(long ip_addr, short port)
|
||
|
|
{
|
||
|
|
memset(&m_SockAddr, 0, sizeof(m_SockAddr));
|
||
|
|
m_SockAddr.sin_addr.s_addr = ip_addr;
|
||
|
|
m_SockAddr.sin_port = htons( port );
|
||
|
|
m_SockAddr.sin_family = AF_INET;
|
||
|
|
}
|
||
|
|
|
||
|
|
void UDPClient::SetBroadcast(SOCKET socket)
|
||
|
|
{
|
||
|
|
// If this fails, we'll hear about it when we try to send.
|
||
|
|
int broadcastPermission = 1;
|
||
|
|
setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (char *) &broadcastPermission, sizeof(broadcastPermission));
|
||
|
|
}
|
||
|
|
|
||
|
|
unsigned long UDPClient::checksum(char *buffer, int size)
|
||
|
|
{
|
||
|
|
unsigned long cksum=0;
|
||
|
|
|
||
|
|
while(size > 1) { cksum+=*buffer++; size -= 2; }
|
||
|
|
if(size) cksum += *(UCHAR*)buffer;
|
||
|
|
|
||
|
|
cksum = (cksum >> 16) + (cksum & 0xffff);
|
||
|
|
cksum += (cksum >>16);
|
||
|
|
return (~cksum);
|
||
|
|
}
|
||
|
|
|
||
|
|
void UDPClient::RecvThread()
|
||
|
|
{
|
||
|
|
int received;
|
||
|
|
EnbUdpHeader *header;
|
||
|
|
short s_null = 0;
|
||
|
|
|
||
|
|
m_recv_thread_running = true;
|
||
|
|
|
||
|
|
while (!g_ServerShutdown && m_recv_thread_running)
|
||
|
|
{
|
||
|
|
received = UDP_RecvFromServer((char*)m_RecvBuffer, MAX_UDPC_BUFFER);
|
||
|
|
|
||
|
|
if (received > 0)
|
||
|
|
{
|
||
|
|
header = (EnbUdpHeader*)m_RecvBuffer;
|
||
|
|
|
||
|
|
unsigned short bytes = header->size - sizeof(EnbUdpHeader);
|
||
|
|
short opcode = header->opcode;
|
||
|
|
long player_id = header->player_id;
|
||
|
|
char *msg = (char*)(m_RecvBuffer + sizeof(EnbUdpHeader));
|
||
|
|
//unsigned long crc = checksum(msg, bytes);
|
||
|
|
|
||
|
|
// Make sure we recived the correct # of byes
|
||
|
|
if (received == (int)(bytes + sizeof(EnbUdpHeader)))
|
||
|
|
{
|
||
|
|
switch (opcode)
|
||
|
|
{
|
||
|
|
case ENB_OPCODE_1001_MVAS_LOGIN_S_C:
|
||
|
|
m_logged_in = true;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ENB_OPCODE_1007_MVAS_TOGGLE_SEND_FREQ_S_C:
|
||
|
|
ToggleSendFrequency(msg);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ENB_OPCODE_1009_MVAS_BAD_LOGIN_S_C:
|
||
|
|
SignalWrongVersion(msg);
|
||
|
|
m_recv_thread_running = false;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ENB_OPCODE_100A_MVAS_TERMINATE_S_C:
|
||
|
|
TerminateClient(msg);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ENB_OPCODE_2001_ACCOUNTVALIDATE:
|
||
|
|
ValidateAccount(msg, header);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ENB_OPCODE_2003_AVATARLIST:
|
||
|
|
ReceiveAvatarList(msg, header);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ENB_OPCODE_2005_AVATARLOGIN_CONFIRM:
|
||
|
|
AvatarLoginConfirm(msg, header);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ENB_OPCODE_2009_MASTER_HANDOFF_CONFIRM:
|
||
|
|
MasterLoginConfirm(msg, header);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ENB_OPCODE_200C_CREATE_DELETE_AVATAR_CONFIRM:
|
||
|
|
CreateDeleteConfirm(msg, header);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ENB_OPCODE_200E_GLOBAL_ERROR:
|
||
|
|
ProcessGlobalError(msg, header);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ENB_OPCODE_2010_DATA_FILE:
|
||
|
|
SendClientDataFile(msg, header);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ENB_OPCODE_2016_PACKET_SEQUENCE:
|
||
|
|
SendPacketSequence((char*)m_RecvBuffer, header);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ENB_OPCODE_2011_GALAXY_MAP_CACHE:
|
||
|
|
SendCachedGalaxyMap();
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ENB_OPCODE_2012_START_PROSPECT:
|
||
|
|
StartProspecting(msg, 0, s_null);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ENB_OPCODE_2013_TRACTOR_ORE:
|
||
|
|
TractorOre(msg, 0, s_null);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ENB_OPCODE_2014_LOOT_ITEM:
|
||
|
|
LootItem(msg, 0, s_null);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ENB_OPCODE_3006_PLAYER_LOGIN_FAILED_CONFIRM:
|
||
|
|
LoginFailAck();
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
ProcessClientOpcode(msg, header);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
LogMessage("Message read failure... opcode = 0x%04x length = 0x%x (rcvd = %x)\n",opcode, bytes, received);
|
||
|
|
LogMessage("Opcode 0x%04x: Length: 0x%04x\n", opcode, bytes);
|
||
|
|
LogMessage("Resend opcode.\n");
|
||
|
|
ReSend send;
|
||
|
|
send.packet_count = 1;
|
||
|
|
send.packet_start = header->packet_sequence;
|
||
|
|
g_ServerMgr->m_UDPConnection->ForwardClientOpcode(ENB_OPCODE_2017_RESEND_PACKET_SEQUENCE, sizeof(ReSend), (char*)&send);
|
||
|
|
//DumpBuffer((unsigned char*)msg, received - sizeof(EnbUdpHeader));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
closesocket(m_Listen_Socket);
|
||
|
|
|
||
|
|
_endthread();
|
||
|
|
}
|
||
|
|
|
||
|
|
int UDPClient::UDP_RecvFromServer(char *buffer, int size)
|
||
|
|
{
|
||
|
|
int rtn;
|
||
|
|
|
||
|
|
if ((rtn = recv(m_Listen_Socket, buffer, size, 0)) < 0)
|
||
|
|
{
|
||
|
|
Sleep(200); //little pause in here if we get a -1
|
||
|
|
//fprintf(stderr,"Received failed (recv()) %d\n",rtn);
|
||
|
|
}
|
||
|
|
|
||
|
|
return rtn;
|
||
|
|
}
|
||
|
|
|
||
|
|
void UDPClient::SignalWrongVersion(char *msg)
|
||
|
|
{
|
||
|
|
float version = (float)*((long *) &msg[0]);
|
||
|
|
|
||
|
|
version = version*0.01f;
|
||
|
|
char msg2[256];
|
||
|
|
|
||
|
|
sprintf(msg2, "Incompatible version of Net7Proxy.\nLatest version is %.2f", version);
|
||
|
|
::MessageBox(NULL, msg2, "Net7Proxy", MB_ICONERROR);
|
||
|
|
|
||
|
|
fprintf(stderr,"\nIncompatible version of Net7Proxy.");
|
||
|
|
fprintf(stderr,"\nYou need to upgrade to version %.2f",version);
|
||
|
|
fprintf(stderr,"\nMake sure you are using the latest LanuchNet7,\nIt should auto-patch the Net7Proxy to the latest version.\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
bool UDPClient::VerifyConnection()
|
||
|
|
{
|
||
|
|
long count = 5;
|
||
|
|
|
||
|
|
long version = VERSION_N;
|
||
|
|
|
||
|
|
LogMessage("Sending login.\n");
|
||
|
|
|
||
|
|
while (count > 0 && m_logged_in == false)
|
||
|
|
{
|
||
|
|
SendResponse(MVAS_LOGIN_PORT, ENB_OPCODE_1000_MVAS_REGISTER_C_S, (unsigned char *) &version, sizeof(version));
|
||
|
|
Sleep(1000);
|
||
|
|
count--;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (m_logged_in)
|
||
|
|
{
|
||
|
|
LogMessage("login OK\n");
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
LogMessage("login failed - server not found.\n");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
void UDPClient::SendResponse(short port, short opcode, unsigned char *data, size_t length)
|
||
|
|
{
|
||
|
|
EnbUdpHeader * header = (EnbUdpHeader *) m_SendBuffer;
|
||
|
|
header->size = (short) length + sizeof(EnbUdpHeader);
|
||
|
|
header->opcode = opcode;
|
||
|
|
header->player_id = m_PlayerID;
|
||
|
|
if (length)
|
||
|
|
{
|
||
|
|
memcpy(m_SendBuffer + sizeof(EnbUdpHeader), data, length);
|
||
|
|
}
|
||
|
|
|
||
|
|
int bytes = length + sizeof(EnbUdpHeader);
|
||
|
|
|
||
|
|
//LogMessage("Sending1 opcode 0x%04x\n", opcode);
|
||
|
|
|
||
|
|
UDP_Send(port, (char*)&m_SendBuffer[0], bytes);
|
||
|
|
}
|
||
|
|
|
||
|
|
void UDPClient::SendResponse(long player_id, short port, short opcode, unsigned char *data, size_t length)
|
||
|
|
{
|
||
|
|
EnbUdpHeader * header = (EnbUdpHeader *) m_SendBuffer;
|
||
|
|
header->size = (short) length + sizeof(EnbUdpHeader);
|
||
|
|
header->opcode = opcode;
|
||
|
|
header->player_id = player_id;
|
||
|
|
|
||
|
|
if (length)
|
||
|
|
{
|
||
|
|
memcpy(m_SendBuffer + sizeof(EnbUdpHeader), data, length);
|
||
|
|
}
|
||
|
|
|
||
|
|
int bytes = length + sizeof(EnbUdpHeader);
|
||
|
|
|
||
|
|
//LogMessage("Sending2 opcode 0x%04x\n", opcode);
|
||
|
|
|
||
|
|
UDP_Send(port, (char*)&m_SendBuffer[0], bytes);
|
||
|
|
}
|
||
|
|
|
||
|
|
void UDPClient::UDP_Send(short port, const char *buffer, int bufferLen)
|
||
|
|
{
|
||
|
|
int buffsend;
|
||
|
|
|
||
|
|
switch (m_ConnectionType)
|
||
|
|
{
|
||
|
|
case CLIENT_TYPE_MULTI_PORT:
|
||
|
|
{
|
||
|
|
SOCKADDR_IN lSockAddr;
|
||
|
|
memset(&lSockAddr,0, sizeof(lSockAddr));
|
||
|
|
lSockAddr.sin_family = AF_INET;
|
||
|
|
lSockAddr.sin_port = htons(port);
|
||
|
|
lSockAddr.sin_addr.s_addr = m_IPAddr; //inet_addr(m_IPAddr);
|
||
|
|
|
||
|
|
// Write out the whole buffer as a single message.
|
||
|
|
if (buffsend = sendto(m_Listen_Socket, buffer, bufferLen, 0,
|
||
|
|
(sockaddr *) &lSockAddr, sizeof(lSockAddr)) != bufferLen)
|
||
|
|
{
|
||
|
|
fprintf(stderr,"\nSend failed %d %d", bufferLen, buffsend);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
case CLIENT_TYPE_FIXED_PORT:
|
||
|
|
{
|
||
|
|
buffsend = send(m_Listen_Socket, buffer, bufferLen, 0);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void UDPClient::WaitForResponse()
|
||
|
|
{
|
||
|
|
int count = 20;
|
||
|
|
while (count > 0 && m_global_account_rcv == false)
|
||
|
|
{
|
||
|
|
for (int i = 0; i < 4; i++)
|
||
|
|
{
|
||
|
|
if (m_global_account_rcv) break;
|
||
|
|
Sleep(250);
|
||
|
|
}
|
||
|
|
count--;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void UDPClient::WaitForResponse(short port, short opcode, unsigned char *data, size_t length)
|
||
|
|
{
|
||
|
|
int count = 20;
|
||
|
|
while (count > 0 && m_global_account_rcv == false)
|
||
|
|
{
|
||
|
|
SendResponse(port, opcode, data, length);
|
||
|
|
for (int i = 0; i < 4; i++)
|
||
|
|
{
|
||
|
|
if (m_global_account_rcv) break;
|
||
|
|
Sleep(250);
|
||
|
|
}
|
||
|
|
count--;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void UDPClient::RecordLastHandoff(char *msg, short bytes)
|
||
|
|
{
|
||
|
|
memset(&m_Server_handoff, 0, sizeof(m_Server_handoff));
|
||
|
|
memcpy(&m_Server_handoff, msg, bytes);
|
||
|
|
}
|