network: start network before starting game to configure eeprom/flash

do second net sync when comm board is reset
close menu/cancel loading when pressing menu button
This commit is contained in:
Flyinghead 2020-04-22 19:11:49 +02:00
parent 65f156817c
commit bec7a9f452
8 changed files with 399 additions and 138 deletions

View File

@ -22,6 +22,7 @@
#include "naomi_regs.h"
#include "hw/holly/sb.h"
#include "hw/sh4/sh4_mem.h"
#include "network/naomi_network.h"
#include <chrono>
static inline u16 swap16(u16 w)
@ -32,7 +33,7 @@ static inline u16 swap16(u16 w)
void NaomiM3Comm::closeNetwork()
{
network_stopping = true;
network.shutdown();
naomiNetwork.shutdown();
if (thread && thread->joinable())
thread->join();
}
@ -40,17 +41,17 @@ void NaomiM3Comm::closeNetwork()
void NaomiM3Comm::connectNetwork()
{
packet_number = 0;
if (network.startNetwork())
if (naomiNetwork.syncNetwork())
{
slot_count = network.slotCount();
slot_id = network.slotId();
slot_count = naomiNetwork.slotCount();
slot_id = naomiNetwork.slotId();
connectedState(true);
}
else
{
connectedState(false);
network_stopping = true;
network.shutdown();
naomiNetwork.shutdown();
}
}
@ -61,7 +62,7 @@ void NaomiM3Comm::receiveNetwork()
std::unique_ptr<u8[]> buf(new u8[packet_size]);
if (network.receive(buf.get(), packet_size))
if (naomiNetwork.receive(buf.get(), packet_size))
{
packet_number += slot_count - 1;
*(u16*)&comm_ram[6] = swap16(packet_number);
@ -72,11 +73,11 @@ void NaomiM3Comm::receiveNetwork()
void NaomiM3Comm::sendNetwork()
{
if (network.hasToken())
if (naomiNetwork.hasToken())
{
const u32 packet_size = swap16(*(u16*)&m68k_ram[0x204]) * slot_count;
std::unique_lock<std::mutex> lock(mem_mutex);
network.send(&comm_ram[0x100], packet_size);
naomiNetwork.send(&comm_ram[0x100], packet_size);
packet_number++;
*(u16*)&comm_ram[6] = swap16(packet_number);
}
@ -85,7 +86,7 @@ void NaomiM3Comm::sendNetwork()
NaomiM3Comm::~NaomiM3Comm()
{
closeNetwork();
network.terminate();
naomiNetwork.terminate();
}
u32 NaomiM3Comm::ReadMem(u32 address, u32 size)
@ -194,10 +195,10 @@ void NaomiM3Comm::WriteMem(u32 address, u32 data, u32 size)
if (data & (1 << 5))
{
DEBUG_LOG(NAOMI, "NAOMI_COMM2_CTRL m68k reset");
closeNetwork();
memset(&comm_ram[0], 0, 32);
comm_status0 = 0; // varies...
comm_status1 = 0;
if (!thread || !thread->joinable())
startThread();
}
comm_ctrl = (u16)(data & ~(1 << 5));
@ -280,10 +281,10 @@ void NaomiM3Comm::startThread()
while (!network_stopping)
{
network.pipeSlaves();
naomiNetwork.pipeSlaves();
receiveNetwork();
if (slot_id == 0 && network.hasToken())
if (slot_id == 0 && naomiNetwork.hasToken())
{
const auto target_duration = std::chrono::milliseconds(10);
auto duration = the_clock::now() - token_time;

View File

@ -24,7 +24,6 @@
#include <memory>
#include <mutex>
#include <thread>
#include "network/naomi_network.h"
class NaomiM3Comm
{
@ -57,5 +56,4 @@ private:
std::atomic<bool> network_stopping{ false };
std::unique_ptr<std::thread> thread;
std::mutex mem_mutex;
NaomiNetwork network;
};

View File

@ -21,6 +21,7 @@
#include "naomi_network.h"
#include "types.h"
#include <array>
#include <chrono>
#include <thread>
#include "rend/gui.h"
@ -31,13 +32,15 @@
typedef int ssize_t;
#endif
NaomiNetwork naomiNetwork;
sock_t NaomiNetwork::createAndBind(int protocol)
{
sock_t sock = socket(AF_INET, protocol == IPPROTO_TCP ? SOCK_STREAM : SOCK_DGRAM, protocol);
if (sock == INVALID_SOCKET)
if (!VALID(sock))
{
ERROR_LOG(NETWORK, "Cannot create server socket");
return INVALID_SOCKET;
return sock;
}
int option = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&option, sizeof(option));
@ -50,8 +53,7 @@ sock_t NaomiNetwork::createAndBind(int protocol)
if (::bind(sock, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
{
ERROR_LOG(NETWORK, "NaomiServer: bind() failed. errno=%d", get_last_error());
closesocket(sock);
sock = INVALID_SOCKET;
closeSocket(sock);
}
else
set_non_blocking(sock);
@ -79,18 +81,17 @@ bool NaomiNetwork::init()
bool NaomiNetwork::createServerSocket()
{
if (server_sock != INVALID_SOCKET)
if (VALID(server_sock))
return true;
server_sock = createAndBind(IPPROTO_TCP);
if (server_sock == INVALID_SOCKET)
if (!VALID(server_sock))
return false;
if (listen(server_sock, 5) < 0)
{
ERROR_LOG(NETWORK, "NaomiServer: listen() failed. errno=%d", get_last_error());
closesocket(server_sock);
server_sock = INVALID_SOCKET;
closeSocket(server_sock);
return false;
}
return true;
@ -98,10 +99,10 @@ bool NaomiNetwork::createServerSocket()
bool NaomiNetwork::createBeaconSocket()
{
if (beacon_sock == INVALID_SOCKET)
if (!VALID(beacon_sock))
beacon_sock = createAndBind(IPPROTO_UDP);
return beacon_sock != INVALID_SOCKET;
return VALID(beacon_sock);
}
void NaomiNetwork::processBeacon()
@ -132,7 +133,7 @@ bool NaomiNetwork::findServer()
{
// Automatically find the adhoc server on the local network using broadcast
sock_t sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == INVALID_SOCKET)
if (!VALID(sockfd))
{
ERROR_LOG(NETWORK, "Datagram socket creation error. errno=%d", get_last_error());
return false;
@ -147,8 +148,8 @@ bool NaomiNetwork::findServer()
return false;
}
// Set a 1 sec timeout on recv call
if (!set_recv_timeout(sockfd, 1000))
// Set a 500ms timeout on recv call
if (!set_recv_timeout(sockfd, 500))
{
ERROR_LOG(NETWORK, "setsockopt(SO_RCVTIMEO) failed. errno=%d", get_last_error());
closesocket(sockfd);
@ -203,7 +204,6 @@ bool NaomiNetwork::findServer()
bool NaomiNetwork::startNetwork()
{
network_stopping = false;
if (!init())
return false;
@ -213,7 +213,7 @@ bool NaomiNetwork::startNetwork()
got_token = false;
using namespace std::chrono;
const auto timeout = seconds(10);
const auto timeout = seconds(20);
if (settings.network.ActAsServer)
{
@ -224,9 +224,9 @@ bool NaomiNetwork::startNetwork()
{
if (network_stopping)
{
for (auto clientSock : slaves)
if (clientSock != INVALID_SOCKET)
closesocket(clientSock);
for (auto& slave : slaves)
if (VALID(slave.socket))
closeSocket(slave.socket);
return false;
}
std::string notif = slaves.empty() ? "Waiting for players..."
@ -239,7 +239,7 @@ bool NaomiNetwork::startNetwork()
socklen_t addr_len = sizeof(src_addr);
memset(&src_addr, 0, addr_len);
sock_t clientSock = accept(server_sock, (struct sockaddr *)&src_addr, &addr_len);
if (clientSock == INVALID_SOCKET)
if (!VALID(clientSock))
{
if (get_last_error() != L_EAGAIN && get_last_error() != L_EWOULDBLOCK)
perror("accept");
@ -247,11 +247,58 @@ bool NaomiNetwork::startNetwork()
else
{
NOTICE_LOG(NETWORK, "Slave connection accepted");
set_non_blocking(clientSock);
set_tcp_nodelay(clientSock);
std::lock_guard<std::mutex> lock(mutex);
slaves.push_back(clientSock);
if (slaves.size() == 3)
break;
slaves.emplace_back(clientSock);
}
const auto now = steady_clock::now();
u32 waiting_slaves = 0;
for (auto& slave : slaves)
{
if (slave.state == ClientState::Waiting)
waiting_slaves++;
else if (slave.state == ClientState::Connected)
{
u8 buffer[8];
ssize_t l = ::recv(slave.socket, buffer, sizeof(buffer), 0);
if (l < (int)sizeof(buffer) && get_last_error() != EAGAIN && get_last_error() != EWOULDBLOCK)
{
// error
INFO_LOG(NETWORK, "Slave socket recv error. errno=%d", get_last_error());
closeSocket(slave.socket);
}
else if (l == -1 && now - slave.state_time > milliseconds(100))
{
// timeout
INFO_LOG(NETWORK, "Slave socket Connected timeout");
closeSocket(slave.socket);
}
else if (l == (int)sizeof(buffer))
{
if (memcmp(buffer, naomi_game_id, sizeof(buffer)))
{
// wrong game
WARN_LOG(NETWORK, "Wrong game id received: %.8s", buffer);
closeSocket(slave.socket);
}
else
{
slave.set_state(ClientState::Waiting);
waiting_slaves++;
}
}
}
}
{
std::lock_guard<std::mutex> lock(mutex);
slaves.erase(std::remove_if(slaves.begin(),
slaves.end(),
[](const Slave& slave){ return !VALID(slave.socket); }),
slaves.end());
}
if (waiting_slaves == 3 || (start_now && !slaves.empty() && waiting_slaves == slaves.size()))
break;
std::this_thread::sleep_for(milliseconds(100));
}
slot_id = 0;
@ -259,22 +306,27 @@ bool NaomiNetwork::startNetwork()
u8 buf[2] = { (u8)slot_count, 0 };
int slot_num = 1;
{
for (int socket : slaves)
for (auto& slave : slaves)
{
buf[1] = { (u8)slot_num };
slot_num++;
::send(socket, (const char *)buf, 2, 0);
set_non_blocking(socket);
set_tcp_nodelay(socket);
::send(slave.socket, (const char *)buf, 2, 0);
slave.set_state(ClientState::Starting);
}
}
NOTICE_LOG(NETWORK, "Master starting: %zd slaves", slaves.size());
if (slot_count > 1)
if (!slaves.empty())
{
gui_display_notification("Starting game", 2000);
else
gui_display_notification("No player connected", 8000);
SetNaomiNetworkConfig(0);
return !slaves.empty();
return true;
}
else
{
gui_display_notification("No player connected", 8000);
return false;
}
}
else
{
@ -296,8 +348,7 @@ bool NaomiNetwork::startNetwork()
gui_display_notification("Connecting to server", 10000);
steady_clock::time_point start_time = steady_clock::now();
while (client_sock == INVALID_SOCKET && !network_stopping
&& steady_clock::now() - start_time < timeout)
while (!network_stopping && steady_clock::now() - start_time < timeout)
{
if (server_ip.s_addr == INADDR_NONE && !findServer())
continue;
@ -310,20 +361,20 @@ bool NaomiNetwork::startNetwork()
if (::connect(client_sock, (struct sockaddr *)&src_addr, sizeof(src_addr)) < 0)
{
ERROR_LOG(NETWORK, "Socket connect failed");
closesocket(client_sock);
client_sock = INVALID_SOCKET;
closeSocket(client_sock);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
else
{
gui_display_notification("Waiting for server to start", 10000);
set_tcp_nodelay(client_sock);
::send(client_sock, naomi_game_id, 8, 0);
set_recv_timeout(client_sock, (int)std::chrono::milliseconds(timeout * 2).count());
u8 buf[2];
if (::recv(client_sock, (char *)buf, 2, 0) < 2)
{
ERROR_LOG(NETWORK, "recv failed: errno=%d", get_last_error());
closesocket(client_sock);
client_sock = INVALID_SOCKET;
closeSocket(client_sock);
gui_display_notification("Server failed to start", 10000);
return false;
@ -331,10 +382,11 @@ bool NaomiNetwork::startNetwork()
slot_count = buf[0];
slot_id = buf[1];
got_token = slot_id == 1;
set_tcp_nodelay(client_sock);
set_non_blocking(client_sock);
std::string notif = "Connected as slot " + std::to_string(slot_id);
gui_display_notification(notif.c_str(), 2000);
SetNaomiNetworkConfig(slot_id);
return true;
}
@ -343,6 +395,109 @@ bool NaomiNetwork::startNetwork()
}
}
bool NaomiNetwork::syncNetwork()
{
using namespace std::chrono;
const auto timeout = seconds(10);
if (settings.network.ActAsServer)
{
steady_clock::time_point start_time = steady_clock::now();
bool all_slaves_ready = false;
while (steady_clock::now() - start_time < timeout && !all_slaves_ready)
{
all_slaves_ready = true;
for (auto& slave : slaves)
if (slave.state != ClientState::Ready)
{
u8 buf[4];
ssize_t l = ::recv(slave.socket, buf, sizeof(buf), 0);
if (l < 4 && get_last_error() != EAGAIN && get_last_error() != EWOULDBLOCK)
{
INFO_LOG(NETWORK, "Socket recv failed. errno=%d", get_last_error());
closeSocket(slave.socket);
return false;
}
if (l == 4)
{
if (memcmp(buf, "REDY", 4))
{
INFO_LOG(NETWORK, "Synchronization failed");
closeSocket(slave.socket);
return false;
}
slave.set_state(ClientState::Ready);
}
else
all_slaves_ready = false;
}
if (network_stopping)
return false;
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
for (auto& slave : slaves)
{
ssize_t l = ::send(slave.socket, "GO!!", 4, 0);
if (l < 4)
{
INFO_LOG(NETWORK, "Socket send failed. errno=%d", get_last_error());
closeSocket(slave.socket);
return false;
}
slave.set_state(ClientState::Online);
}
gui_display_notification("Network started", 5000);
return true;
}
else
{
// Tell master we're ready
ssize_t l = ::send(client_sock, "REDY", 4 ,0);
if (l < 4)
{
WARN_LOG(NETWORK, "Socket send failed. errno=%d", get_last_error());
closeSocket(client_sock);
return false;
}
steady_clock::time_point start_time = steady_clock::now();
while (steady_clock::now() - start_time < timeout)
{
// Wait for the go
u8 buf[4];
l = ::recv(client_sock, buf, sizeof(buf), 0);
if (l < 4 && get_last_error() != EAGAIN && get_last_error() != EWOULDBLOCK)
{
INFO_LOG(NETWORK, "Socket recv failed. errno=%d", get_last_error());
closeSocket(client_sock);
return false;
}
else if (l == 4)
{
if (memcmp(buf, "GO!!", 4))
{
INFO_LOG(NETWORK, "Synchronization failed");
closeSocket(client_sock);
return false;
}
gui_display_notification("Network started", 5000);
return true;
}
if (network_stopping)
{
closeSocket(client_sock);
return false;
}
std::this_thread::sleep_for(std::chrono::milliseconds(20));
}
INFO_LOG(NETWORK, "Socket recv timeout");
closeSocket(client_sock);
return false;
}
}
void NaomiNetwork::pipeSlaves()
{
if (!isMaster() || slot_count < 3)
@ -350,25 +505,23 @@ void NaomiNetwork::pipeSlaves()
char buf[16384];
for (auto it = slaves.begin(); it != slaves.end() - 1; it++)
{
if (*it == INVALID_SOCKET || *(it + 1) == INVALID_SOCKET)
if (!VALID(it->socket) || !VALID((it + 1)->socket))
// TODO keep link on
continue;
ssize_t l = ::recv(*it, buf, sizeof(buf), 0);
ssize_t l = ::recv(it->socket, buf, sizeof(buf), 0);
if (l <= 0)
{
if (get_last_error() == L_EAGAIN || get_last_error() == L_EWOULDBLOCK)
continue;
WARN_LOG(NETWORK, "pipeSlaves: receive failed. errno=%d", get_last_error());
closesocket(*it);
*it = INVALID_SOCKET;
closeSocket(it->socket);
continue;
}
ssize_t l2 = ::send(*(it + 1), buf, l, 0);
ssize_t l2 = ::send((it + 1)->socket, buf, l, 0);
if (l2 != l)
{
WARN_LOG(NETWORK, "pipeSlaves: send failed. errno=%d", get_last_error());
closesocket(*(it + 1));
*(it + 1) = INVALID_SOCKET;
closeSocket((it + 1)->socket);
}
}
}
@ -377,10 +530,10 @@ bool NaomiNetwork::receive(u8 *data, u32 size)
{
sock_t sockfd = INVALID_SOCKET;
if (isMaster())
sockfd = slaves.empty() ? INVALID_SOCKET : slaves.back();
sockfd = slaves.empty() ? INVALID_SOCKET : slaves.back().socket;
else
sockfd = client_sock;
if (sockfd == INVALID_SOCKET)
if (!VALID(sockfd))
return false;
ssize_t received = 0;
@ -394,8 +547,7 @@ bool NaomiNetwork::receive(u8 *data, u32 size)
WARN_LOG(NETWORK, "receiveNetwork: read failed. errno=%d", get_last_error());
if (isMaster())
{
slaves.back() = INVALID_SOCKET;
closesocket(sockfd);
closeSocket(slaves.back().socket);
got_token = false;
}
else
@ -422,10 +574,10 @@ void NaomiNetwork::send(u8 *data, u32 size)
sock_t sockfd;
if (isMaster())
sockfd = slaves.empty() ? INVALID_SOCKET : slaves.front();
sockfd = slaves.empty() ? INVALID_SOCKET : slaves.front().socket;
else
sockfd = client_sock;
if (sockfd == INVALID_SOCKET)
if (!VALID(sockfd))
return;
if (::send(sockfd, (const char *)data, size, 0) < size)
@ -434,10 +586,7 @@ void NaomiNetwork::send(u8 *data, u32 size)
{
WARN_LOG(NETWORK, "send failed. errno=%d", get_last_error());
if (isMaster())
{
slaves.front() = INVALID_SOCKET;
closesocket(sockfd);
}
closeSocket(slaves.front().socket);
else
shutdown();
}
@ -455,32 +604,29 @@ void NaomiNetwork::shutdown()
network_stopping = true;
{
std::lock_guard<std::mutex> lock(mutex);
for (auto& sock : slaves)
{
closesocket(sock);
sock = INVALID_SOCKET;
}
}
if (client_sock != INVALID_SOCKET)
{
closesocket(client_sock);
client_sock = INVALID_SOCKET;
for (auto& slave : slaves)
closeSocket(slave.socket);
}
if (VALID(client_sock))
closeSocket(client_sock);
}
void NaomiNetwork::terminate()
{
shutdown();
if (beacon_sock != INVALID_SOCKET)
{
closesocket(beacon_sock);
beacon_sock = INVALID_SOCKET;
if (VALID(beacon_sock))
closeSocket(beacon_sock);
if (VALID(server_sock))
closeSocket(server_sock);
}
if (server_sock != INVALID_SOCKET)
std::future<bool> NaomiNetwork::startNetworkAsync()
{
closesocket(server_sock);
server_sock = INVALID_SOCKET;
}
network_stopping = false;
start_now = false;
return std::async(std::launch::async, [this] {
return startNetwork();
});
}
// Sets the game network config using MIE eeprom or bbsram:
@ -549,3 +695,19 @@ void SetNaomiNetworkConfig(int node)
write_naomi_flash(0x220, node == 0 ? 0 : 1); // node id
}
}
bool NaomiNetworkSupported()
{
static const std::array<const char *, 12> games = {
"ALIEN FRONT", "MOBILE SUIT GUNDAM JAPAN", "MOBILE SUIT GUNDAM DELUXE JAPAN", " BIOHAZARD GUN SURVIVOR2",
"HEAVY METAL JAPAN", "OUTTRIGGER JAPAN", "SLASHOUT JAPAN VERSION", "SPAWN JAPAN",
"SPIKERS BATTLE JAPAN VERSION", "VIRTUAL-ON ORATORIO TANGRAM", "WAVE RUNNER GP", "WORLD KICKS"
};
if (!settings.network.Enable)
return false;
for (auto game : games)
if (!strcmp(game, naomi_game_id))
return true;
return false;
}

View File

@ -22,6 +22,7 @@
#include "types.h"
#include <cstdint>
#include <atomic>
#include <future>
#include <mutex>
#include <vector>
#include "net_platform.h"
@ -37,8 +38,9 @@ public:
#endif
}
~NaomiNetwork() { terminate(); }
bool init();
bool startNetwork();
std::future<bool> startNetworkAsync();
void startNow() { start_now = true; }
bool syncNetwork();
void pipeSlaves();
bool receive(u8 *data, u32 size);
void send(u8 *data, u32 size);
@ -49,19 +51,32 @@ public:
bool hasToken() const { return got_token; }
private:
bool init();
bool createServerSocket();
bool createBeaconSocket();
bool startNetwork();
void processBeacon();
bool findServer();
sock_t createAndBind(int protocol);
bool isMaster() const { return slot_id == 0; }
void closeSocket(sock_t& socket) const { closesocket(socket); socket = INVALID_SOCKET; }
struct in_addr server_ip;
std::string server_name;
// server stuff
sock_t server_sock = INVALID_SOCKET;
sock_t beacon_sock = INVALID_SOCKET;
std::vector<sock_t> slaves;
enum class ClientState { Connected, Waiting, Starting, Ready, Online };
struct Slave {
Slave(sock_t socket)
: state(ClientState::Connected), state_time(std::chrono::steady_clock::now()), socket(socket) {}
void set_state(ClientState state) { this->state = state; this->state_time = std::chrono::steady_clock::now(); }
ClientState state;
std::chrono::steady_clock::time_point state_time;
sock_t socket;
};
std::vector<Slave> slaves;
bool start_now = false;
// client stuff
sock_t client_sock = INVALID_SOCKET;
// common stuff
@ -73,5 +88,7 @@ private:
static const uint16_t SERVER_PORT = 37391;
};
extern NaomiNetwork naomiNetwork;
void SetNaomiNetworkConfig(int node);
bool NaomiNetworkSupported();

View File

@ -638,8 +638,7 @@ static void dc_start_game(const char *path)
if (settings.platform.system == DC_PLATFORM_NAOMI)
{
mcfg_CreateNAOMIJamma();
SetNaomiNetworkConfig(settings.network.Enable ? settings.network.ActAsServer ? 0 : 1 // FIXME more than 2 nodes
: -1);
SetNaomiNetworkConfig(-1);
}
else if (settings.platform.system == DC_PLATFORM_ATOMISWAVE)
mcfg_CreateAtomisWaveControllers();
@ -1096,8 +1095,6 @@ static void cleanup_serialize(void *data)
{
if ( data != NULL )
free(data) ;
dc_resume();
}
static std::string get_savestate_file_path()
@ -1275,6 +1272,7 @@ void dc_cancel_load()
loading_canceled = true;
loading_done.get();
}
settings.imgread.ImagePath[0] = '\0';
}
void dc_get_load_status()

View File

@ -17,6 +17,7 @@
along with reicast. If not, see <https://www.gnu.org/licenses/>.
*/
#include <mutex>
#include "gui.h"
#include "cfg/cfg.h"
#include "hw/maple/maple_if.h"
@ -36,6 +37,7 @@
#include "imgread/common.h"
#include "log/LogManager.h"
#include "emulator.h"
#include "network/naomi_network.h"
extern void UpdateInputState(u32 port);
extern bool game_started;
@ -56,6 +58,7 @@ static bool touch_up;
static std::string error_msg;
static std::string osd_message;
static double osd_message_end;
static std::mutex osd_message_mutex;
static void display_vmus();
static void reset_vmus();
@ -207,7 +210,6 @@ void ImGui_Impl_NewFrame()
io.NavInputs[ImGuiNavInput_Activate] = (kcode[0] & DC_BTN_A) == 0;
io.NavInputs[ImGuiNavInput_Cancel] = (kcode[0] & DC_BTN_B) == 0;
io.NavInputs[ImGuiNavInput_Input] = (kcode[0] & DC_BTN_X) == 0;
//io.NavInputs[ImGuiNavInput_Menu] = (kcode[0] & DC_BTN_Y) == 0;
io.NavInputs[ImGuiNavInput_DpadLeft] = (kcode[0] & DC_DPAD_LEFT) == 0;
io.NavInputs[ImGuiNavInput_DpadRight] = (kcode[0] & DC_DPAD_RIGHT) == 0;
io.NavInputs[ImGuiNavInput_DpadUp] = (kcode[0] & DC_DPAD_UP) == 0;
@ -282,6 +284,16 @@ void gui_open_settings()
{
gui_state = VJoyEditCommands;
}
else if (gui_state == Loading)
{
dc_cancel_load();
gui_state = Main;
}
else if (gui_state == Commands)
{
gui_state = Closed;
dc_resume();
}
}
static void gui_start_game(const std::string& path)
@ -1447,6 +1459,20 @@ static void gui_display_settings()
settings.dynarec.Enable = (bool)dynarec_enabled;
}
void gui_display_notification(const char *msg, int duration)
{
std::lock_guard<std::mutex> lock(osd_message_mutex);
osd_message = msg;
osd_message_end = os_GetSeconds() + (double)duration / 1000.0;
}
static std::string get_notification()
{
std::lock_guard<std::mutex> lock(osd_message_mutex);
if (!osd_message.empty() && os_GetSeconds() >= osd_message_end)
osd_message.clear();
return osd_message;
}
static void gui_display_demo()
{
@ -1498,7 +1524,6 @@ static void gui_display_content()
if (ImGui::Selectable("Dreamcast BIOS"))
{
gui_state = Closed;
settings.imgread.ImagePath[0] = '\0';
gui_start_game("");
}
ImGui::PopID();
@ -1551,7 +1576,7 @@ static void gui_display_content()
ImGui_impl_RenderDrawData(ImGui::GetDrawData(), false);
}
void systemdir_selected_callback(bool cancelled, std::string selection)
static void systemdir_selected_callback(bool cancelled, std::string selection)
{
if (!cancelled)
{
@ -1570,7 +1595,7 @@ void systemdir_selected_callback(bool cancelled, std::string selection)
}
}
void gui_display_onboarding()
static void gui_display_onboarding()
{
ImGui_Impl_NewFrame();
ImGui::NewFrame();
@ -1582,7 +1607,71 @@ void gui_display_onboarding()
ImGui_impl_RenderDrawData(ImGui::GetDrawData(), false);
}
void gui_display_loadscreen()
static std::future<bool> networkStatus;
static void start_network()
{
networkStatus = naomiNetwork.startNetworkAsync();
gui_state = NetworkStart;
}
static void gui_network_start()
{
ImGui_Impl_NewFrame();
ImGui::NewFrame();
ImGui::SetNextWindowPos(ImVec2(screen_width / 2, screen_height / 2), ImGuiCond_Always, ImVec2(0.5f, 0.5f));
ImGui::SetNextWindowSize(ImVec2(330 * scaling, 180 * scaling));
ImGui::Begin("##network", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize);
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(20 * scaling, 10 * scaling));
ImGui::AlignTextToFramePadding();
ImGui::SetCursorPosX(20.f * scaling);
if (networkStatus.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready)
{
if (networkStatus.get())
{
gui_state = Closed;
ImGui::Text("STARTING...");
}
else
{
gui_state = Main;
settings.imgread.ImagePath[0] = '\0';
}
}
else
{
ImGui::Text("STARTING NETWORK...");
if (settings.network.ActAsServer)
ImGui::Text("Press Start to start the game now.");
}
ImGui::Text("%s", get_notification().c_str());
float currentwidth = ImGui::GetContentRegionAvailWidth();
ImGui::SetCursorPosX((currentwidth - 100.f * scaling) / 2.f + ImGui::GetStyle().WindowPadding.x);
ImGui::SetCursorPosY(126.f * scaling);
if (ImGui::Button("Cancel", ImVec2(100.f * scaling, 0.f)))
{
naomiNetwork.terminate();
networkStatus.get();
gui_state = Main;
settings.imgread.ImagePath[0] = '\0';
}
ImGui::PopStyleVar();
ImGui::End();
ImGui::Render();
ImGui_impl_RenderDrawData(ImGui::GetDrawData(), false);
if ((kcode[0] & DC_BTN_START) == 0)
naomiNetwork.startNow();
}
static void gui_display_loadscreen()
{
ImGui_Impl_NewFrame();
ImGui::NewFrame();
@ -1599,9 +1688,16 @@ void gui_display_loadscreen()
{
try {
dc_get_load_status();
if (NaomiNetworkSupported())
{
start_network();
}
else
{
gui_state = Closed;
ImGui::Text("STARTING...");
} catch (ReicastException& ex) {
}
} catch (const ReicastException& ex) {
ERROR_LOG(BOOT, "%s", ex.reason.c_str());
error_msg = ex.reason;
#ifdef TEST_AUTOMATION
@ -1614,17 +1710,9 @@ void gui_display_loadscreen()
else
{
ImGui::Text("LOADING... ");
double now = os_GetSeconds();
if (!osd_message.empty())
{
if (now >= osd_message_end)
osd_message.clear();
else
{
ImGui::SameLine();
ImGui::Text("%s", osd_message.c_str());
}
}
ImGui::Text("%s", get_notification().c_str());
float currentwidth = ImGui::GetContentRegionAvailWidth();
ImGui::SetCursorPosX((currentwidth - 100.f * scaling) / 2.f + ImGui::GetStyle().WindowPadding.x);
ImGui::SetCursorPosY(126.f * scaling);
@ -1632,7 +1720,6 @@ void gui_display_loadscreen()
{
dc_cancel_load();
gui_state = Main;
settings.imgread.ImagePath[0] = '\0';
}
}
ImGui::PopStyleVar();
@ -1681,6 +1768,12 @@ void gui_display_ui()
case Loading:
gui_display_loadscreen();
break;
case NetworkStart:
gui_network_start();
break;
default:
die("Unknown UI state");
break;
}
if (gui_state == Closed)
@ -1693,12 +1786,6 @@ static float fps = -1;
extern bool fast_forward_mode;
void gui_display_notification(const char *msg, int duration)
{
osd_message = msg;
osd_message_end = os_GetSeconds() + (double)duration / 1000.0;
}
static std::string getFPSNotification()
{
if (settings.rend.ShowFPS)
@ -1723,19 +1810,9 @@ void gui_display_osd()
{
if (gui_state == VJoyEdit)
return;
double now = os_GetSeconds();
if (!osd_message.empty())
{
if (now >= osd_message_end)
osd_message.clear();
}
std::string message;
if (osd_message.empty())
{
std::string message = get_notification();
if (message.empty())
message = getFPSNotification();
}
else
message = osd_message;
if (!message.empty() || settings.rend.FloatVMUs)
{

View File

@ -33,7 +33,18 @@ extern u32 vmu_lcd_data[8][48 * 32];
extern bool vmu_lcd_status[8];
extern bool vmu_lcd_changed[8];
typedef enum { Closed, Commands, Settings, Main, Onboarding, VJoyEdit, VJoyEditCommands, SelectDisk, Loading } GuiState;
typedef enum {
Closed,
Commands,
Settings,
Main,
Onboarding,
VJoyEdit,
VJoyEditCommands,
SelectDisk,
Loading,
NetworkStart
} GuiState;
extern GuiState gui_state;
void ImGui_Impl_NewFrame();

View File

@ -24,11 +24,8 @@
#include "types.h"
#include "stdclass.h"
#include "imgui/imgui.h"
//#include "gles/imgui_impl_opengl3.h"
#include "gui_util.h"
extern bool settings_opening;
void vjoy_reset_editing();
void vjoy_stop_editing(bool canceled);