mirror of
https://github.com/melonDS-emu/melonDS.git
synced 2025-06-27 17:39:41 -06:00
1086 lines
28 KiB
C++
1086 lines
28 KiB
C++
/*
|
|
Copyright 2016-2024 melonDS team
|
|
|
|
This file is part of melonDS.
|
|
|
|
melonDS is free software: you can redistribute it and/or modify it under
|
|
the terms of the GNU General Public License as published by the Free
|
|
Software Foundation, either version 3 of the License, or (at your option)
|
|
any later version.
|
|
|
|
melonDS 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 General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License along
|
|
with melonDS. If not, see http://www.gnu.org/licenses/.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <queue>
|
|
|
|
#include <enet/enet.h>
|
|
|
|
#include "NDS.h"
|
|
#include "NDSCart.h"
|
|
#include "main.h"
|
|
//#include "IPC.h"
|
|
#include "Netplay.h"
|
|
//#include "Input.h"
|
|
//#include "ROMManager.h"
|
|
//#include "Config.h"
|
|
#include "Savestate.h"
|
|
#include "Platform.h"
|
|
|
|
using namespace melonDS;
|
|
|
|
namespace Netplay
|
|
{
|
|
|
|
bool Active;
|
|
bool IsHost;
|
|
bool IsMirror;
|
|
|
|
ENetHost* Host;
|
|
ENetHost* MirrorHost;
|
|
|
|
Player Players[16];
|
|
int NumPlayers;
|
|
|
|
Player MyPlayer;
|
|
u32 HostAddress;
|
|
bool Lag;
|
|
|
|
int NumMirrorClients;
|
|
|
|
struct InputFrame
|
|
{
|
|
u32 FrameNum;
|
|
u32 KeyMask;
|
|
u32 Touching;
|
|
u32 TouchX, TouchY;
|
|
};
|
|
|
|
std::queue<InputFrame> InputQueue;
|
|
|
|
enum
|
|
{
|
|
Blob_CartROM = 0,
|
|
Blob_CartSRAM,
|
|
Blob_InitState,
|
|
|
|
Blob_MAX
|
|
};
|
|
|
|
const u32 kChunkSize = 0x10000;
|
|
u8 ChunkBuffer[0x10 + kChunkSize];
|
|
u8* Blobs[Blob_MAX];
|
|
u32 BlobLens[Blob_MAX];
|
|
int CurBlobType;
|
|
u32 CurBlobLen;
|
|
|
|
|
|
bool Init()
|
|
{
|
|
Active = false;
|
|
IsHost = false;
|
|
IsMirror = false;
|
|
Host = nullptr;
|
|
MirrorHost = nullptr;
|
|
Lag = false;
|
|
|
|
memset(Players, 0, sizeof(Players));
|
|
NumPlayers = 0;
|
|
|
|
NumMirrorClients = 0;
|
|
|
|
for (int i = 0; i < Blob_MAX; i++)
|
|
{
|
|
Blobs[i] = nullptr;
|
|
BlobLens[i] = 0;
|
|
}
|
|
CurBlobType = -1;
|
|
CurBlobLen = 0;
|
|
|
|
/*if (enet_initialize() != 0)
|
|
{
|
|
printf("enet shat itself :(\n");
|
|
return false;
|
|
}
|
|
|
|
printf("enet init OK\n");*/
|
|
return true;
|
|
}
|
|
|
|
void DeInit()
|
|
{
|
|
// TODO: cleanup resources properly!!
|
|
|
|
//enet_deinitialize();
|
|
}
|
|
|
|
|
|
void StartHost(const char* playername, int port)
|
|
{
|
|
ENetAddress addr;
|
|
addr.host = ENET_HOST_ANY;
|
|
addr.port = port;
|
|
|
|
Host = enet_host_create(&addr, 16, 1, 0, 0);
|
|
if (!Host)
|
|
{
|
|
printf("host shat itself :(\n");
|
|
return;
|
|
}
|
|
|
|
Player* player = &Players[0];
|
|
memset(player, 0, sizeof(Player));
|
|
player->ID = 0;
|
|
strncpy(player->Name, playername, 31);
|
|
player->Status = 2;
|
|
player->Address = 0x0100007F;
|
|
NumPlayers = 1;
|
|
memcpy(&MyPlayer, player, sizeof(Player));
|
|
|
|
HostAddress = 0x0100007F;
|
|
|
|
NumMirrorClients = 0;
|
|
|
|
ENetAddress mirroraddr;
|
|
mirroraddr.host = ENET_HOST_ANY;
|
|
mirroraddr.port = port + 1;
|
|
printf("host mirror host connecting to %08X:%d\n", mirroraddr.host, mirroraddr.port);
|
|
MirrorHost = enet_host_create(&mirroraddr, 16, 2, 0, 0);
|
|
if (!MirrorHost)
|
|
{
|
|
printf("mirror host shat itself :(\n");
|
|
return;
|
|
}
|
|
|
|
Active = true;
|
|
IsHost = true;
|
|
IsMirror = false;
|
|
|
|
//netplayDlg->updatePlayerList(Players, NumPlayers);
|
|
}
|
|
|
|
void StartClient(const char* playername, const char* host, int port)
|
|
{
|
|
Host = enet_host_create(nullptr, 1, 1, 0, 0);
|
|
if (!Host)
|
|
{
|
|
printf("client shat itself :(\n");
|
|
return;
|
|
}
|
|
|
|
printf("client created, connecting (%s, %s:%d)\n", playername, host, port);
|
|
|
|
ENetAddress addr;
|
|
enet_address_set_host(&addr, host);
|
|
addr.port = port;
|
|
ENetPeer* peer = enet_host_connect(Host, &addr, 1, 0);
|
|
if (!peer)
|
|
{
|
|
printf("connect shat itself :(\n");
|
|
return;
|
|
}
|
|
|
|
ENetEvent event;
|
|
bool conn = false;
|
|
if (enet_host_service(Host, &event, 5000) > 0)
|
|
{
|
|
if (event.type == ENET_EVENT_TYPE_CONNECT)
|
|
{
|
|
printf("connected!\n");
|
|
conn = true;
|
|
}
|
|
}
|
|
|
|
if (!conn)
|
|
{
|
|
printf("connection failed\n");
|
|
enet_peer_reset(peer);
|
|
return;
|
|
}
|
|
|
|
Player* player = &MyPlayer;
|
|
memset(player, 0, sizeof(Player));
|
|
player->ID = 0;
|
|
strncpy(player->Name, playername, 31);
|
|
player->Status = 3;
|
|
|
|
HostAddress = addr.host;
|
|
|
|
Active = true;
|
|
IsHost = false;
|
|
IsMirror = false;
|
|
}
|
|
|
|
void StartMirror(const Player* player)
|
|
{
|
|
for (int i = 0; i < Blob_MAX; i++)
|
|
{
|
|
Blobs[i] = nullptr;
|
|
BlobLens[i] = 0;
|
|
}
|
|
CurBlobType = -1;
|
|
CurBlobLen = 0;
|
|
|
|
MirrorHost = enet_host_create(nullptr, 1, 2, 0, 0);
|
|
if (!MirrorHost)
|
|
{
|
|
printf("mirror shat itself :(\n");
|
|
return;
|
|
}
|
|
|
|
printf("mirror created, connecting\n");
|
|
|
|
ENetAddress addr;
|
|
addr.host = player->Address;
|
|
addr.port = 8064+1 + player->ID; // FIXME!!!!!!!!!!
|
|
printf("mirror client connecting to %08X:%d\n", addr.host, addr.port);
|
|
ENetPeer* peer = enet_host_connect(MirrorHost, &addr, 2, 0);
|
|
if (!peer)
|
|
{
|
|
printf("connect shat itself :(\n");
|
|
return;
|
|
}
|
|
|
|
ENetEvent event;
|
|
bool conn = false;
|
|
if (enet_host_service(MirrorHost, &event, 5000) > 0)
|
|
{
|
|
if (event.type == ENET_EVENT_TYPE_CONNECT)
|
|
{
|
|
printf("connected!\n");
|
|
conn = true;
|
|
}
|
|
}
|
|
|
|
if (!conn)
|
|
{
|
|
printf("connection failed\n");
|
|
enet_peer_reset(peer);
|
|
return;
|
|
}
|
|
|
|
memcpy(&MyPlayer, player, sizeof(Player));
|
|
|
|
HostAddress = addr.host;
|
|
|
|
Active = true;
|
|
IsHost = false;
|
|
IsMirror = true;
|
|
}
|
|
|
|
|
|
u32 PlayerAddress(int id)
|
|
{
|
|
if (id < 0 || id > 16) return 0;
|
|
|
|
u32 ret = Players[id].Address;
|
|
if (ret == 0x0100007F) ret = HostAddress;
|
|
return ret;
|
|
}
|
|
|
|
|
|
bool SpawnMirrorInstance(Player player)
|
|
{
|
|
#if 0
|
|
u16 curmask = IPC::GetInstanceBitmask();
|
|
|
|
QProcess newinst;
|
|
newinst.setProgram(QApplication::applicationFilePath());
|
|
newinst.setArguments(QApplication::arguments().mid(1, QApplication::arguments().length()-1));
|
|
|
|
#ifdef __WIN32__
|
|
newinst.setCreateProcessArgumentsModifier([] (QProcess::CreateProcessArguments *args)
|
|
{
|
|
args->flags |= CREATE_NEW_CONSOLE;
|
|
});
|
|
#endif
|
|
|
|
if (!newinst.startDetached())
|
|
return false;
|
|
|
|
// try to determine the ID of the new instance
|
|
|
|
int newid = -1;
|
|
for (int tries = 0; tries < 10; tries++)
|
|
{
|
|
QThread::usleep(100 * 1000);
|
|
|
|
u16 newmask = IPC::GetInstanceBitmask();
|
|
if (newmask == curmask) continue;
|
|
|
|
newmask &= ~curmask;
|
|
for (int id = 0; id < 16; id++)
|
|
{
|
|
if (newmask & (1 << id))
|
|
{
|
|
newid = id;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (newid == -1) return false;
|
|
|
|
// setup that instance
|
|
printf("netplay: spawned mirror instance for player %d with ID %d, configuring\n", player.ID, newid);
|
|
|
|
//std::string rompath = ROMManager::FullROMPath.join('|').toStdString();
|
|
//IPC::SendCommandStr(1<<newid, IPC::Cmd_LoadROM, rompath);
|
|
|
|
if (player.Address == 0x0100007F) player.Address = HostAddress;
|
|
// calls Netplay::StartMirror()
|
|
//IPC::SendCommand(1<<newid, IPC::Cmd_SetupNetplayMirror, sizeof(Player), &player);
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
bool SendBlobToMirrorClients(int type, u32 len, u8* data)
|
|
{
|
|
u8* buf = ChunkBuffer;
|
|
|
|
buf[0] = 0x01;
|
|
buf[1] = type & 0xFF;
|
|
buf[2] = 0;
|
|
buf[3] = 0;
|
|
*(u32*)&buf[4] = len;
|
|
|
|
ENetPacket* pkt = enet_packet_create(buf, 8, ENET_PACKET_FLAG_RELIABLE);
|
|
enet_host_broadcast(MirrorHost, 1, pkt);
|
|
|
|
if (len > 0)
|
|
{
|
|
buf[0] = 0x02;
|
|
*(u32*)&buf[12] = 0;
|
|
|
|
for (u32 pos = 0; pos < len; pos += kChunkSize)
|
|
{
|
|
u32 chunklen = kChunkSize;
|
|
if ((pos + chunklen) > len)
|
|
chunklen = len - pos;
|
|
|
|
*(u32*)&buf[8] = pos;
|
|
memcpy(&buf[16], &data[pos], chunklen);
|
|
|
|
ENetPacket* pkt = enet_packet_create(buf, 16+chunklen, ENET_PACKET_FLAG_RELIABLE);
|
|
enet_host_broadcast(MirrorHost, 1, pkt);
|
|
//enet_host_flush(MirrorHost);
|
|
}
|
|
}
|
|
|
|
buf[0] = 0x03;
|
|
|
|
pkt = enet_packet_create(buf, 8, ENET_PACKET_FLAG_RELIABLE);
|
|
enet_host_broadcast(MirrorHost, 1, pkt);
|
|
|
|
return true;
|
|
}
|
|
|
|
void RecvBlobFromMirrorHost(ENetPeer* peer, ENetPacket* pkt)
|
|
{
|
|
u8* buf = pkt->data;
|
|
if (buf[0] == 0x01)
|
|
{
|
|
if (CurBlobType != -1) return;
|
|
if (pkt->dataLength != 8) return;
|
|
|
|
int type = buf[1];
|
|
if (type > Blob_MAX) return;
|
|
|
|
u32 len = *(u32*)&buf[4];
|
|
if (len > 0x40000000) return;
|
|
|
|
if (Blobs[type] != nullptr) return;
|
|
if (BlobLens[type] != 0) return;
|
|
printf("[MC] start blob type=%d len=%d\n", type, len);
|
|
if (len) Blobs[type] = new u8[len];
|
|
BlobLens[type] = len;
|
|
|
|
CurBlobType = type;
|
|
CurBlobLen = len;
|
|
|
|
ENetEvent evt;
|
|
while (enet_host_service(MirrorHost, &evt, 5000) > 0)
|
|
{
|
|
if (evt.type == ENET_EVENT_TYPE_RECEIVE && evt.channelID == 1)
|
|
{
|
|
RecvBlobFromMirrorHost(evt.peer, evt.packet);
|
|
if (evt.packet->dataLength >= 1 && evt.packet->data[0] == 0x03)
|
|
{
|
|
printf("[MC] blob done while in fast recv loop\n");
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
printf("[MC] fast recv loop aborted because evt %d ch %d\n", evt.type, evt.channelID);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (buf[0] == 0x02)
|
|
{
|
|
if (CurBlobType < 0 || CurBlobType > Blob_MAX) return;
|
|
if (pkt->dataLength > (16+kChunkSize)) return;
|
|
|
|
int type = buf[1];
|
|
if (type != CurBlobType) return;
|
|
|
|
u32 len = *(u32*)&buf[4];
|
|
if (len != CurBlobLen) return;
|
|
|
|
u32 pos = *(u32*)&buf[8];
|
|
if (pos >= len) return;
|
|
if ((pos + (pkt->dataLength-16)) > len) return;
|
|
|
|
u8* dst = Blobs[type];
|
|
if (!dst) return;
|
|
if (BlobLens[type] != len) return;
|
|
printf("[MC] recv blob data, type=%d pos=%08X len=%08X data=%08X\n", type, pos, len, pkt->dataLength-16);
|
|
memcpy(&dst[pos], &buf[16], pkt->dataLength-16);
|
|
}
|
|
else if (buf[0] == 0x03)
|
|
{
|
|
if (CurBlobType < 0 || CurBlobType > Blob_MAX) return;
|
|
if (pkt->dataLength != 8) return;
|
|
|
|
int type = buf[1];
|
|
if (type != CurBlobType) return;
|
|
|
|
u32 len = *(u32*)&buf[4];
|
|
if (len != CurBlobLen) return;
|
|
printf("[MC] finish blob type=%d len=%d\n", type, len);
|
|
CurBlobType = -1;
|
|
CurBlobLen = 0;
|
|
}
|
|
else if (buf[0] == 0x04)
|
|
{
|
|
if (pkt->dataLength != 2) return;
|
|
|
|
bool res = false;
|
|
#if 0
|
|
// reset
|
|
NDS::SetConsoleType(buf[1]);
|
|
NDS::EjectCart();
|
|
NDS::Reset();
|
|
//SetBatteryLevels();
|
|
|
|
if (Blobs[Blob_CartROM])
|
|
{
|
|
res = NDS::LoadCart(Blobs[Blob_CartROM], BlobLens[Blob_CartROM],
|
|
Blobs[Blob_CartSRAM], BlobLens[Blob_CartSRAM]);
|
|
if (!res)
|
|
{
|
|
printf("!!!! FAIL!!\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (res)
|
|
{
|
|
ROMManager::CartType = 0;
|
|
//ROMManager::NDSSave = new SaveManager(savname);
|
|
|
|
//LoadCheats();
|
|
}
|
|
#endif
|
|
// load initial state
|
|
// TODO: terrible hack!!
|
|
#if 0
|
|
FILE* f = Platform::OpenFile("netplay2.mln", "wb");
|
|
fwrite(Blobs[Blob_InitState], BlobLens[Blob_InitState], 1, f);
|
|
fclose(f);
|
|
Savestate* state = new Savestate("netplay2.mln", false);
|
|
NDS::DoSavestate(state);
|
|
delete state;
|
|
|
|
for (int i = 0; i < Blob_MAX; i++)
|
|
{
|
|
if (Blobs[i]) delete[] Blobs[i];
|
|
Blobs[i] = nullptr;
|
|
BlobLens[i] = 0;
|
|
}
|
|
|
|
/*Savestate* zorp = new Savestate("netplay3.mln", true);
|
|
NDS::DoSavestate(zorp);
|
|
delete zorp;*/
|
|
|
|
printf("[MC] state loaded, PC=%08X/%08X\n", NDS::GetPC(0), NDS::GetPC(1));
|
|
ENetPacket* resp = enet_packet_create(buf, 1, ENET_PACKET_FLAG_RELIABLE);
|
|
enet_peer_send(peer, 1, resp);
|
|
#endif
|
|
}
|
|
else if (buf[0] == 0x05)
|
|
{
|
|
printf("[MIRROR CLIENT] start\n");
|
|
StartLocal();
|
|
}
|
|
}
|
|
|
|
void SyncMirrorClients()
|
|
{
|
|
printf("[MIRROR HOST] syncing clients\n");
|
|
|
|
#if 0
|
|
SendBlobToMirrorClients(Blob_CartSRAM, NDSCart::GetSaveMemoryLength(), NDSCart::GetSaveMemory());
|
|
|
|
// send initial state
|
|
// TODO: this is a terrible hack!
|
|
/*printf("[MH] state start\n");
|
|
Savestate* state = new Savestate("netplay.mln", true);
|
|
NDS::DoSavestate(state);
|
|
delete state;
|
|
printf("[MH] state taken: PC=%08X/%08X\n", NDS::GetPC(0), NDS::GetPC(1));
|
|
FILE* f = Platform::OpenLocalFile("netplay.mln", "rb");
|
|
printf("[MH] state=%d\n", f?1:0);
|
|
fseek(f, 0, SEEK_END);
|
|
u32 flen = ftell(f);
|
|
fseek(f, 0, SEEK_SET);
|
|
u8* statebuf = new u8[flen];
|
|
fread(statebuf, flen, 1, f);
|
|
fclose(f);
|
|
printf("[MH] state read, len=%d\n", flen);
|
|
SendBlobToMirrorClients(Blob_InitState, flen, statebuf);
|
|
printf("[MH] state sent\n");
|
|
delete[] statebuf;*/
|
|
|
|
u8 data[2];
|
|
data[0] = 0x04;
|
|
data[1] = (u8)Config::ConsoleType;
|
|
ENetPacket* pkt = enet_packet_create(&data, 2, ENET_PACKET_FLAG_RELIABLE);
|
|
enet_host_broadcast(MirrorHost, 1, pkt);
|
|
//enet_host_flush(MirrorHost);
|
|
|
|
// wait for all clients to have caught up
|
|
int ngood = 0;
|
|
ENetEvent evt;
|
|
while (enet_host_service(MirrorHost, &evt, 300000) > 0)
|
|
{printf("EVENT %d CH %d\n", evt.type, evt.channelID);
|
|
if (evt.type == ENET_EVENT_TYPE_RECEIVE && evt.channelID == 1)
|
|
{
|
|
if (evt.packet->dataLength == 1 && evt.packet->data[0] == 0x04)
|
|
ngood++;
|
|
}
|
|
else
|
|
break;
|
|
|
|
if (ngood >= (NumPlayers-1))
|
|
break;
|
|
}
|
|
|
|
if (ngood != (NumPlayers-1))
|
|
printf("!!! BAD!! %d %d\n", ngood, NumPlayers);
|
|
|
|
printf("[MIRROR HOST] clients synced\n");
|
|
|
|
// start
|
|
|
|
data[0] = 0x05;
|
|
pkt = enet_packet_create(&data, 1, ENET_PACKET_FLAG_RELIABLE);
|
|
enet_host_broadcast(MirrorHost, 1, pkt);
|
|
//enet_host_flush(MirrorHost);
|
|
|
|
StartLocal();
|
|
#endif
|
|
}
|
|
|
|
void StartGame()
|
|
{
|
|
if (!IsHost)
|
|
{
|
|
printf("?????\n");
|
|
return;
|
|
}
|
|
|
|
// spawn mirror instances as needed
|
|
for (int i = 1; i < NumPlayers; i++)
|
|
{
|
|
SpawnMirrorInstance(Players[i]);
|
|
}
|
|
|
|
//SyncMirrorClients();
|
|
|
|
// tell remote peers to start game
|
|
u8 cmd[1] = {0x04};
|
|
ENetPacket* pkt = enet_packet_create(cmd, sizeof(cmd), ENET_PACKET_FLAG_RELIABLE);
|
|
enet_host_broadcast(Host, 0, pkt);
|
|
|
|
// tell other mirror instances to start the game
|
|
//IPC::SendCommand(0xFFFF, IPC::Cmd_Start, 0, nullptr);
|
|
|
|
// TO START MIRROR CLIENT SHITO
|
|
//
|
|
// 1. NDS::Reset()
|
|
// 2. load ROM
|
|
// 3. load state
|
|
|
|
// start game locally
|
|
//StartLocal();
|
|
}
|
|
|
|
void StartLocal()
|
|
{
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
InputFrame frame;
|
|
frame.FrameNum = i;
|
|
frame.KeyMask = 0xFFF;
|
|
frame.Touching = 0;
|
|
frame.TouchX = 0;
|
|
frame.TouchY = 0;
|
|
InputQueue.push(frame);
|
|
}
|
|
|
|
//NDS::Start();
|
|
//emuThread->emuRun();
|
|
}
|
|
|
|
|
|
void ProcessHost()
|
|
{
|
|
if (!Host) return;
|
|
|
|
ENetEvent event;
|
|
while (enet_host_service(Host, &event, 0) > 0)
|
|
{
|
|
switch (event.type)
|
|
{
|
|
case ENET_EVENT_TYPE_CONNECT:
|
|
{
|
|
// client connected; assign player number
|
|
|
|
int id;
|
|
for (id = 0; id < 16; id++)
|
|
{
|
|
if (id >= NumPlayers) break;
|
|
if (Players[id].Status == 0) break;
|
|
}
|
|
|
|
if (id < 16)
|
|
{
|
|
u8 cmd[2];
|
|
cmd[0] = 0x01;
|
|
cmd[1] = (u8)id;
|
|
ENetPacket* pkt = enet_packet_create(cmd, 2, ENET_PACKET_FLAG_RELIABLE);
|
|
enet_peer_send(event.peer, 0, pkt);
|
|
|
|
Players[id].ID = id;
|
|
Players[id].Status = 3;
|
|
Players[id].Address = event.peer->address.host;
|
|
event.peer->data = &Players[id];
|
|
NumPlayers++;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ENET_EVENT_TYPE_DISCONNECT:
|
|
{
|
|
// TODO
|
|
printf("disco\n");
|
|
}
|
|
break;
|
|
|
|
case ENET_EVENT_TYPE_RECEIVE:
|
|
{
|
|
if (event.packet->dataLength < 1) break;
|
|
|
|
u8* data = (u8*)event.packet->data;
|
|
switch (data[0])
|
|
{
|
|
case 0x02: // client sending player info
|
|
{
|
|
if (event.packet->dataLength != (1+sizeof(Player))) break;
|
|
|
|
Player player;
|
|
memcpy(&player, &data[1], sizeof(Player));
|
|
player.Name[31] = '\0';
|
|
|
|
Player* hostside = (Player*)event.peer->data;
|
|
if (player.ID != hostside->ID)
|
|
{
|
|
printf("what??? %d =/= %d\n", player.ID, hostside->ID);
|
|
// TODO: disconnect
|
|
break;
|
|
}
|
|
|
|
player.Status = 1;
|
|
player.Address = event.peer->address.host;
|
|
memcpy(hostside, &player, sizeof(Player));
|
|
|
|
// broadcast updated player list
|
|
u8 cmd[2+sizeof(Players)];
|
|
cmd[0] = 0x03;
|
|
cmd[1] = (u8)NumPlayers;
|
|
memcpy(&cmd[2], Players, sizeof(Players));
|
|
ENetPacket* pkt = enet_packet_create(cmd, 2+sizeof(Players), ENET_PACKET_FLAG_RELIABLE);
|
|
enet_host_broadcast(Host, 0, pkt);
|
|
|
|
//netplayDlg->updatePlayerList(Players, NumPlayers);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ProcessClient()
|
|
{
|
|
if (!Host) return;
|
|
|
|
ENetEvent event;
|
|
while (enet_host_service(Host, &event, 0) > 0)
|
|
{
|
|
switch (event.type)
|
|
{
|
|
case ENET_EVENT_TYPE_CONNECT:
|
|
printf("schmo\n");
|
|
break;
|
|
|
|
case ENET_EVENT_TYPE_DISCONNECT:
|
|
{
|
|
// TODO
|
|
printf("shma\n");
|
|
}
|
|
break;
|
|
|
|
case ENET_EVENT_TYPE_RECEIVE:
|
|
{
|
|
if (event.packet->dataLength < 1) break;
|
|
|
|
u8* data = (u8*)event.packet->data;
|
|
switch (data[0])
|
|
{
|
|
case 0x01: // host sending player ID
|
|
{
|
|
if (event.packet->dataLength != 2) break;
|
|
|
|
NumMirrorClients = 0;
|
|
|
|
// create mirror host
|
|
ENetAddress mirroraddr;
|
|
mirroraddr.host = ENET_HOST_ANY;
|
|
mirroraddr.port = 8064+1 + data[1]; // FIXME!!!!
|
|
printf("client mirror host connecting to %08X:%d\n", mirroraddr.host, mirroraddr.port);
|
|
MirrorHost = enet_host_create(&mirroraddr, 16, 2, 0, 0);
|
|
if (!MirrorHost)
|
|
{
|
|
printf("mirror host shat itself :(\n");
|
|
break;
|
|
}
|
|
|
|
// send player information
|
|
MyPlayer.ID = data[1];
|
|
u8 cmd[1+sizeof(Player)];
|
|
cmd[0] = 0x02;
|
|
memcpy(&cmd[1], &MyPlayer, sizeof(Player));
|
|
ENetPacket* pkt = enet_packet_create(cmd, 1+sizeof(Player), ENET_PACKET_FLAG_RELIABLE);
|
|
enet_peer_send(event.peer, 0, pkt);
|
|
}
|
|
break;
|
|
|
|
case 0x03: // host sending player list
|
|
{
|
|
if (event.packet->dataLength != (2+sizeof(Players))) break;
|
|
if (data[1] > 16) break;
|
|
|
|
NumPlayers = data[1];
|
|
memcpy(Players, &data[2], sizeof(Players));
|
|
for (int i = 0; i < 16; i++)
|
|
{
|
|
Players[i].Name[31] = '\0';
|
|
}
|
|
|
|
//netplayDlg->updatePlayerList(Players, NumPlayers);
|
|
}
|
|
break;
|
|
|
|
case 0x04: // start game
|
|
{
|
|
// spawn mirror instances as needed
|
|
for (int i = 0; i < NumPlayers; i++)
|
|
{
|
|
if (i != MyPlayer.ID)
|
|
SpawnMirrorInstance(Players[i]);
|
|
}
|
|
|
|
//SyncMirrorClients();
|
|
printf("bourf\n");
|
|
// tell other mirror instances to start the game
|
|
//IPC::SendCommand(0xFFFF, IPC::Cmd_Start, 0, nullptr);
|
|
printf("birf\n");
|
|
// start game locally
|
|
//StartLocal();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ProcessMirrorHost()
|
|
{
|
|
if (!MirrorHost) return;
|
|
#if 0
|
|
bool block = false;
|
|
ENetEvent event;
|
|
while (enet_host_service(MirrorHost, &event, block ? 5000 : 0) > 0)
|
|
{
|
|
switch (event.type)
|
|
{
|
|
case ENET_EVENT_TYPE_CONNECT:
|
|
printf("[MIRROR HOST] mirror client connected\n");
|
|
NumMirrorClients++;
|
|
event.peer->data = (void*)0;
|
|
|
|
if (NumMirrorClients >= NumPlayers)
|
|
{
|
|
printf("??????\n");
|
|
}
|
|
else if (NumMirrorClients == (NumPlayers-1))
|
|
{
|
|
// all mirror clients are connected, we're ready to go
|
|
SyncMirrorClients();
|
|
//StartLocal();
|
|
}
|
|
break;
|
|
|
|
case ENET_EVENT_TYPE_DISCONNECT:
|
|
{
|
|
// TODO
|
|
printf("[MIRROR HOST] mirror client disconnected\n");
|
|
NumMirrorClients--;
|
|
}
|
|
break;
|
|
|
|
case ENET_EVENT_TYPE_RECEIVE:
|
|
if (event.channelID == 0)
|
|
{
|
|
if (event.packet->dataLength != 4) break;
|
|
/*u8* data = (u8*)event.packet->data;
|
|
|
|
if (data[0])
|
|
{
|
|
event.peer->data = (void*)1;
|
|
block = true;
|
|
}
|
|
else
|
|
{
|
|
event.peer->data = (void*)0;
|
|
block = false;
|
|
|
|
for (int i = 0; i < MirrorHost->peerCount; i++)
|
|
{
|
|
ENetPeer* peer = &(MirrorHost->peers[i]);
|
|
if (peer->state != ENET_PEER_STATE_CONNECTED) continue;
|
|
if (peer->data != (void*)0)
|
|
{
|
|
block = true;
|
|
break;
|
|
}
|
|
}
|
|
}*/
|
|
s32 clientframes = *(s32*)event.packet->data;
|
|
//printf("[SYNC] HOST=%d CLIENT=%d\n", NDS::NumFrames, clientframes);
|
|
if (clientframes < (((s32)NDS::NumFrames) - 16))
|
|
{
|
|
event.peer->data = (void*)1;
|
|
block = true;
|
|
}
|
|
else
|
|
{
|
|
event.peer->data = (void*)0;
|
|
block = false;
|
|
|
|
for (int i = 0; i < MirrorHost->peerCount; i++)
|
|
{
|
|
ENetPeer* peer = &(MirrorHost->peers[i]);
|
|
if (peer->state != ENET_PEER_STATE_CONNECTED) continue;
|
|
if (peer->data != (void*)0)
|
|
{
|
|
block = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void ProcessMirrorClient()
|
|
{
|
|
if (!MirrorHost) return;
|
|
#if 0
|
|
bool block = false;
|
|
if (emuThread->emuIsRunning())// && NDS::NumFrames > 4)
|
|
{
|
|
if (InputQueue.empty())
|
|
block = true;
|
|
}
|
|
|
|
ENetEvent event;
|
|
while (enet_host_service(MirrorHost, &event, block ? 5000 : 0) > 0)
|
|
{
|
|
switch (event.type)
|
|
{
|
|
case ENET_EVENT_TYPE_CONNECT:
|
|
printf("schmu\n");
|
|
Lag = false;
|
|
break;
|
|
|
|
case ENET_EVENT_TYPE_DISCONNECT:
|
|
{
|
|
// TODO
|
|
printf("shmz\n");
|
|
}
|
|
break;
|
|
|
|
case ENET_EVENT_TYPE_RECEIVE://printf("RX %d %d\n", event.channelID, event.packet->dataLength);
|
|
if (event.channelID == 0)
|
|
{
|
|
if (event.packet->dataLength != sizeof(InputFrame)) break;
|
|
|
|
u8* data = (u8*)event.packet->data;
|
|
InputFrame frame;
|
|
memcpy(&frame, data, sizeof(InputFrame));
|
|
InputQueue.push(frame);
|
|
|
|
/*bool lag = (InputQueue.size() > 4*2);
|
|
if (lag != Lag)
|
|
{
|
|
// let the mirror host know they are running too fast for us
|
|
printf("mirror client lag notify: %d\n", lag);
|
|
u8 data = lag ? 1 : 0;
|
|
ENetPacket* pkt = enet_packet_create(&data, 1, ENET_PACKET_FLAG_RELIABLE);
|
|
enet_peer_send(event.peer, 0, pkt);
|
|
|
|
Lag = lag;
|
|
}*/
|
|
{
|
|
ENetPacket* pkt = enet_packet_create(&NDS::NumFrames, 4, ENET_PACKET_FLAG_RELIABLE);
|
|
enet_peer_send(event.peer, 0, pkt);
|
|
//enet_host_flush(MirrorHost);
|
|
}
|
|
}
|
|
else if (event.channelID == 1)
|
|
{
|
|
RecvBlobFromMirrorHost(event.peer, event.packet);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (block) break;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void ProcessFrame()
|
|
{
|
|
if (IsMirror)
|
|
{
|
|
ProcessMirrorClient();
|
|
}
|
|
else
|
|
{
|
|
if (IsHost)
|
|
{
|
|
ProcessHost();
|
|
}
|
|
else
|
|
{
|
|
ProcessClient();
|
|
}
|
|
|
|
ProcessMirrorHost();
|
|
}
|
|
}
|
|
|
|
void ProcessInput()
|
|
{
|
|
// netplay input processing
|
|
//
|
|
// N = current frame #
|
|
// L = amount of lag frames
|
|
//
|
|
// host side:
|
|
// we take the current input (normally meant for frame N)
|
|
// and delay it to frame N+L
|
|
//
|
|
// client side:
|
|
// we receive input from the host
|
|
// apply each input to the frame it's assigned to
|
|
// before running a frame, we need to wait to have received input for it
|
|
// TODO: alert host if we are running too far behind
|
|
#if 0
|
|
if (!IsMirror)
|
|
{
|
|
u32 lag = 4; // TODO: make configurable!!
|
|
|
|
InputFrame frame;
|
|
frame.FrameNum = NDS::NumFrames + lag;
|
|
frame.KeyMask = Input::InputMask;
|
|
frame.Touching = Input::Touching ? 1:0;
|
|
frame.TouchX = Input::TouchX;
|
|
frame.TouchY = Input::TouchY;
|
|
// TODO: other shit! (some hotkeys for example?)
|
|
|
|
InputQueue.push(frame);
|
|
|
|
u8 cmd[sizeof(InputFrame)];
|
|
memcpy(cmd, &frame, sizeof(InputFrame));
|
|
ENetPacket* pkt = enet_packet_create(cmd, sizeof(cmd), ENET_PACKET_FLAG_RELIABLE);
|
|
enet_host_broadcast(MirrorHost, 0, pkt);
|
|
//enet_host_flush(MirrorHost);
|
|
}
|
|
|
|
if (InputQueue.empty())
|
|
{
|
|
//if (NDS::NumFrames > 4)
|
|
printf("Netplay: BAD! INPUT QUEUE EMPTY\n");
|
|
return;
|
|
}
|
|
|
|
InputFrame& frame = InputQueue.front();
|
|
|
|
if (frame.FrameNum < NDS::NumFrames)
|
|
{
|
|
// TODO: this situation is a desync
|
|
printf("Netplay: BAD! LAGGING BEHIND\n");
|
|
while (frame.FrameNum < NDS::NumFrames)
|
|
{
|
|
if (InputQueue.size() < 2) break;
|
|
InputQueue.pop();
|
|
frame = InputQueue.front();
|
|
}
|
|
}
|
|
|
|
if (frame.FrameNum > NDS::NumFrames)
|
|
{
|
|
// frame in the future, ignore
|
|
return;
|
|
}
|
|
|
|
// apply this input frame
|
|
if (frame.KeyMask != 0xFFF) printf("[%08d] INPUT=%08X (%08d) (backlog=%d)\n", NDS::NumFrames, frame.KeyMask, frame.FrameNum, InputQueue.size());
|
|
NDS::SetKeyMask(frame.KeyMask);
|
|
if (frame.Touching) NDS::TouchScreen(frame.TouchX, frame.TouchY);
|
|
else NDS::ReleaseScreen();
|
|
|
|
InputQueue.pop();
|
|
#endif
|
|
}
|
|
|
|
}
|