From bec7a9f4524279be920be69a9b2cc155be6b848d Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Wed, 22 Apr 2020 19:11:49 +0200 Subject: [PATCH] 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 --- core/hw/naomi/naomi_m3comm.cpp | 27 +-- core/hw/naomi/naomi_m3comm.h | 2 - core/network/naomi_network.cpp | 310 +++++++++++++++++++++++++-------- core/network/naomi_network.h | 23 ++- core/nullDC.cpp | 6 +- core/rend/gui.cpp | 153 ++++++++++++---- core/rend/gui.h | 13 +- core/rend/gui_android.cpp | 3 - 8 files changed, 399 insertions(+), 138 deletions(-) diff --git a/core/hw/naomi/naomi_m3comm.cpp b/core/hw/naomi/naomi_m3comm.cpp index 083d5d9f2..898815172 100644 --- a/core/hw/naomi/naomi_m3comm.cpp +++ b/core/hw/naomi/naomi_m3comm.cpp @@ -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 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 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 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,11 +195,11 @@ 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; - startThread(); + if (!thread || !thread->joinable()) + startThread(); } comm_ctrl = (u16)(data & ~(1 << 5)); //DEBUG_LOG(NAOMI, "NAOMI_COMM2_CTRL set to %x", comm_ctrl); @@ -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; diff --git a/core/hw/naomi/naomi_m3comm.h b/core/hw/naomi/naomi_m3comm.h index 12af14121..420647c04 100644 --- a/core/hw/naomi/naomi_m3comm.h +++ b/core/hw/naomi/naomi_m3comm.h @@ -24,7 +24,6 @@ #include #include #include -#include "network/naomi_network.h" class NaomiM3Comm { @@ -57,5 +56,4 @@ private: std::atomic network_stopping{ false }; std::unique_ptr thread; std::mutex mem_mutex; - NaomiNetwork network; }; diff --git a/core/network/naomi_network.cpp b/core/network/naomi_network.cpp index 190fc1135..ab99fe221 100644 --- a/core/network/naomi_network.cpp +++ b/core/network/naomi_network.cpp @@ -21,6 +21,7 @@ #include "naomi_network.h" #include "types.h" +#include #include #include #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 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 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 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 (server_sock != INVALID_SOCKET) - { - closesocket(server_sock); - server_sock = INVALID_SOCKET; - } + if (VALID(beacon_sock)) + closeSocket(beacon_sock); + if (VALID(server_sock)) + closeSocket(server_sock); +} + +std::future NaomiNetwork::startNetworkAsync() +{ + 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 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; +} diff --git a/core/network/naomi_network.h b/core/network/naomi_network.h index f24c609f3..94c383aae 100644 --- a/core/network/naomi_network.h +++ b/core/network/naomi_network.h @@ -22,6 +22,7 @@ #include "types.h" #include #include +#include #include #include #include "net_platform.h" @@ -37,8 +38,9 @@ public: #endif } ~NaomiNetwork() { terminate(); } - bool init(); - bool startNetwork(); + std::future 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 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 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(); diff --git a/core/nullDC.cpp b/core/nullDC.cpp index 22b1c0c62..cec35da65 100755 --- a/core/nullDC.cpp +++ b/core/nullDC.cpp @@ -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() diff --git a/core/rend/gui.cpp b/core/rend/gui.cpp index e65412876..058bbdd8a 100644 --- a/core/rend/gui.cpp +++ b/core/rend/gui.cpp @@ -17,6 +17,7 @@ along with reicast. If not, see . */ +#include #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 lock(osd_message_mutex); + osd_message = msg; + osd_message_end = os_GetSeconds() + (double)duration / 1000.0; +} + +static std::string get_notification() +{ + std::lock_guard 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 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(); - gui_state = Closed; - ImGui::Text("STARTING..."); - } catch (ReicastException& ex) { + if (NaomiNetworkSupported()) + { + start_network(); + } + else + { + gui_state = Closed; + ImGui::Text("STARTING..."); + } + } 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::SameLine(); + 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) { diff --git a/core/rend/gui.h b/core/rend/gui.h index 2ca8634c3..608c3f376 100644 --- a/core/rend/gui.h +++ b/core/rend/gui.h @@ -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(); diff --git a/core/rend/gui_android.cpp b/core/rend/gui_android.cpp index 2826e7efe..d23ff77f7 100644 --- a/core/rend/gui_android.cpp +++ b/core/rend/gui_android.cpp @@ -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);