From dbdc1bb57976f315daa5828fc9189892e8a9c441 Mon Sep 17 00:00:00 2001 From: Bo Qin Date: Sat, 26 Oct 2019 19:24:35 -0700 Subject: [PATCH 1/2] Cross-platform support for *nix platforms --- src/CMakeSources.cmake | 22 +- src/apps/vectorwar/gdi_renderer.cpp | 2 +- src/apps/vectorwar/ggpo_perfmon.cpp | 4 +- src/apps/vectorwar/main.cpp | 6 +- src/apps/vectorwar/vectorwar.cpp | 4 +- src/include/ggponet.h | 47 ++- src/lib/ggpo/backends/p2p.cpp | 2 +- src/lib/ggpo/backends/synctest.cpp | 4 +- src/lib/ggpo/main.cpp | 6 + src/lib/ggpo/network/udp.cpp | 17 +- src/lib/ggpo/network/udp_proto.cpp | 5 + src/lib/ggpo/network/udp_proto.h | 2 +- src/lib/ggpo/pevents.cpp | 603 ++++++++++++++++++++++++++++ src/lib/ggpo/pevents.h | 42 ++ src/lib/ggpo/platform_linux.cpp | 3 + src/lib/ggpo/platform_linux.h | 5 +- src/lib/ggpo/platform_unix.cpp | 29 ++ src/lib/ggpo/platform_unix.h | 59 +++ src/lib/ggpo/platform_windows.h | 2 + src/lib/ggpo/poll.cpp | 12 + src/lib/ggpo/types.h | 2 + src/lib/ggpo/zconf.h | 6 +- 22 files changed, 848 insertions(+), 36 deletions(-) create mode 100644 src/lib/ggpo/pevents.cpp create mode 100644 src/lib/ggpo/pevents.h create mode 100644 src/lib/ggpo/platform_unix.cpp create mode 100644 src/lib/ggpo/platform_unix.h diff --git a/src/CMakeSources.cmake b/src/CMakeSources.cmake index 20685715..f121e292 100644 --- a/src/CMakeSources.cmake +++ b/src/CMakeSources.cmake @@ -24,10 +24,24 @@ set(GGPO_LIB_SRC_NOFILTER ) if(UNIX) - set(GGPO_LIB_SRC_NOFILTER - ${GGPO_LIB_SRC_NOFILTER} - "lib/ggpo/platform_linux.cpp" - ) + if(APPLE) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdeclspec") + set(GGPO_LIB_SRC_NOFILTER + ${GGPO_LIB_SRC_NOFILTER} + "lib/ggpo/platform_unix.h" + "lib/ggpo/platform_unix.cpp" + "lib/ggpo/pevents.h" + "lib/ggpo/pevents.cpp" + ) + else(APPLE) + set(GGPO_LIB_SRC_NOFILTER + ${GGPO_LIB_SRC_NOFILTER} + "lib/ggpo/platform_linux.h" + "lib/ggpo/platform_linux.cpp" + "lib/ggpo/pevents.h" + "lib/ggpo/pevents.cpp" + ) + endif(APPLE) endif() set(GGPO_LIB_INC_NETWORK diff --git a/src/apps/vectorwar/gdi_renderer.cpp b/src/apps/vectorwar/gdi_renderer.cpp index 4cb62af2..8d879165 100644 --- a/src/apps/vectorwar/gdi_renderer.cpp +++ b/src/apps/vectorwar/gdi_renderer.cpp @@ -168,7 +168,7 @@ GDIRenderer::DrawConnectState(HDC hdc, Ship &ship, PlayerConnectionInfo &info, C case Disconnecting: sprintf(status, "Waiting for player..."); - progress = (timeGetTime() - info.disconnect_start) * 100 / info.disconnect_timeout; + progress = (Platform::GetCurrentTimeMS() - info.disconnect_start) * 100 / info.disconnect_timeout; break; } diff --git a/src/apps/vectorwar/ggpo_perfmon.cpp b/src/apps/vectorwar/ggpo_perfmon.cpp index 98b41430..90d539d4 100644 --- a/src/apps/vectorwar/ggpo_perfmon.cpp +++ b/src/apps/vectorwar/ggpo_perfmon.cpp @@ -114,7 +114,7 @@ ggpo_perfmon_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) case WM_INITDIALOG: { char pid[64]; - sprintf(pid, "%d", GetCurrentProcessId()); + sprintf(pid, "%d", Platform::GetProcessId()); SetWindowTextA(GetDlgItem(hwndDlg, IDC_PID), pid); return TRUE; } @@ -210,7 +210,7 @@ ggpoutil_perfmon_update(GGPOSession *ggpo, GGPOPlayerHandle players[], int num_p } } - int now = timeGetTime(); + int now = Platform::GetCurrentTimeMS(); if (_dialog) { InvalidateRect(GetDlgItem(_dialog, IDC_FAIRNESS_GRAPH), NULL, FALSE); InvalidateRect(GetDlgItem(_dialog, IDC_NETWORK_GRAPH), NULL, FALSE); diff --git a/src/apps/vectorwar/main.cpp b/src/apps/vectorwar/main.cpp index 12d56bfd..2eaa9857 100644 --- a/src/apps/vectorwar/main.cpp +++ b/src/apps/vectorwar/main.cpp @@ -48,7 +48,7 @@ CreateMainWindow(HINSTANCE hInstance) int width = 640, height = 480; WCHAR titlebuf[128]; - wsprintf(titlebuf, L"(pid:%d) ggpo sdk sample: vector war", GetCurrentProcessId()); + wsprintf(titlebuf, L"(pid:%d) ggpo sdk sample: vector war", Platform::GetProcessId()); wndclass.cbSize = sizeof(wndclass); wndclass.lpfnWndProc = MainWindowProc; wndclass.lpszClassName = L"vwwnd"; @@ -71,7 +71,7 @@ RunMainLoop(HWND hwnd) MSG msg = { 0 }; int start, next, now; - start = next = now = timeGetTime(); + start = next = now = Platform::GetCurrentTimeMS(); while(1) { while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); @@ -80,7 +80,7 @@ RunMainLoop(HWND hwnd) return; } } - now = timeGetTime(); + now = Platform::GetCurrentTimeMS(); VectorWar_Idle(max(0, next - now - 1)); if (now >= next) { VectorWar_RunFrame(hwnd); diff --git a/src/apps/vectorwar/vectorwar.cpp b/src/apps/vectorwar/vectorwar.cpp index 48c4ddd7..a8e7b7b5 100644 --- a/src/apps/vectorwar/vectorwar.cpp +++ b/src/apps/vectorwar/vectorwar.cpp @@ -82,7 +82,7 @@ vw_on_event_callback(GGPOEvent *info) break; case GGPO_EVENTCODE_CONNECTION_INTERRUPTED: ngs.SetDisconnectTimeout(info->u.connection_interrupted.player, - timeGetTime(), + Platform::GetCurrentTimeMS(), info->u.connection_interrupted.disconnect_timeout); break; case GGPO_EVENTCODE_CONNECTION_RESUMED: @@ -92,7 +92,7 @@ vw_on_event_callback(GGPOEvent *info) ngs.SetConnectState(info->u.disconnected.player, Disconnected); break; case GGPO_EVENTCODE_TIMESYNC: - Sleep(1000 * info->u.timesync.frames_ahead / 60); + Platform::SleepMS(1000 * info->u.timesync.frames_ahead / 60); break; } return true; diff --git a/src/include/ggponet.h b/src/include/ggponet.h index d93de30c..ab35c9b0 100644 --- a/src/include/ggponet.h +++ b/src/include/ggponet.h @@ -8,6 +8,21 @@ #ifndef _GGPONET_H_ #define _GGPONET_H_ +#if defined(_MSC_VER) + // Microsoft + #define EXPORT __declspec(dllexport) +#elif defined(APPLE) || defined(__GNUC__) + // GCC + #define EXPORT __attribute__((visibility("default"))) +#else + // do nothing and hope for the best? + #define EXPORT +#endif + +#ifdef __GNUC__ +#define __cdecl __attribute__((cdecl)) +#endif + #ifdef __cplusplus extern "C" { #endif @@ -303,7 +318,7 @@ typedef struct GGPONetworkStats { * * local_port - The port GGPO should bind to for UDP traffic. */ -__declspec(dllexport) GGPOErrorCode __cdecl ggpo_start_session(GGPOSession **session, +EXPORT GGPOErrorCode __cdecl ggpo_start_session(GGPOSession **session, GGPOSessionCallbacks *cb, const char *game, int num_players, @@ -322,7 +337,7 @@ __declspec(dllexport) GGPOErrorCode __cdecl ggpo_start_session(GGPOSession **ses * handle - An out parameter to a handle used to identify this player in the future. * (e.g. in the on_event callbacks). */ -__declspec(dllexport) GGPOErrorCode __cdecl ggpo_add_player(GGPOSession *session, +EXPORT GGPOErrorCode __cdecl ggpo_add_player(GGPOSession *session, GGPOPlayer *player, GGPOPlayerHandle *handle); @@ -351,7 +366,7 @@ __declspec(dllexport) GGPOErrorCode __cdecl ggpo_add_player(GGPOSession *session * recommended value is 1. * */ -__declspec(dllexport) GGPOErrorCode __cdecl ggpo_start_synctest(GGPOSession **session, +EXPORT GGPOErrorCode __cdecl ggpo_start_synctest(GGPOSession **session, GGPOSessionCallbacks *cb, char *game, int num_players, @@ -383,7 +398,7 @@ __declspec(dllexport) GGPOErrorCode __cdecl ggpo_start_synctest(GGPOSession **se * * host_port - The port of the session on the host */ -__declspec(dllexport) GGPOErrorCode __cdecl ggpo_start_spectating(GGPOSession **session, +EXPORT GGPOErrorCode __cdecl ggpo_start_spectating(GGPOSession **session, GGPOSessionCallbacks *cb, const char *game, int num_players, @@ -397,7 +412,7 @@ __declspec(dllexport) GGPOErrorCode __cdecl ggpo_start_spectating(GGPOSession ** * Used to close a session. You must call ggpo_close_session to * free the resources allocated in ggpo_start_session. */ -__declspec(dllexport) GGPOErrorCode __cdecl ggpo_close_session(GGPOSession *); +EXPORT GGPOErrorCode __cdecl ggpo_close_session(GGPOSession *); /* @@ -406,7 +421,7 @@ __declspec(dllexport) GGPOErrorCode __cdecl ggpo_close_session(GGPOSession *); * Change the amount of frames ggpo will delay local input. Must be called * before the first call to ggpo_synchronize_input. */ -__declspec(dllexport) GGPOErrorCode __cdecl ggpo_set_frame_delay(GGPOSession *, +EXPORT GGPOErrorCode __cdecl ggpo_set_frame_delay(GGPOSession *, GGPOPlayerHandle player, int frame_delay); @@ -419,7 +434,7 @@ __declspec(dllexport) GGPOErrorCode __cdecl ggpo_set_frame_delay(GGPOSession *, * timeout - The amount of time GGPO.net is allowed to spend in this function, * in milliseconds. */ -__declspec(dllexport) GGPOErrorCode __cdecl ggpo_idle(GGPOSession *, +EXPORT GGPOErrorCode __cdecl ggpo_idle(GGPOSession *, int timeout); /* @@ -437,7 +452,7 @@ __declspec(dllexport) GGPOErrorCode __cdecl ggpo_idle(GGPOSession *, * size - The size of the controller inputs. This must be exactly equal to the * size passed into ggpo_start_session. */ -__declspec(dllexport) GGPOErrorCode __cdecl ggpo_add_local_input(GGPOSession *, +EXPORT GGPOErrorCode __cdecl ggpo_add_local_input(GGPOSession *, GGPOPlayerHandle player, void *values, int size); @@ -459,7 +474,7 @@ __declspec(dllexport) GGPOErrorCode __cdecl ggpo_add_local_input(GGPOSession *, * that player will be zeroed and the i-th flag will be set. For example, * if only player 3 has disconnected, disconnect flags will be 8 (i.e. 1 << 3). */ -__declspec(dllexport) GGPOErrorCode __cdecl ggpo_synchronize_input(GGPOSession *, +EXPORT GGPOErrorCode __cdecl ggpo_synchronize_input(GGPOSession *, void *values, int size, int *disconnect_flags); @@ -470,7 +485,7 @@ __declspec(dllexport) GGPOErrorCode __cdecl ggpo_synchronize_input(GGPOSession * * Disconnects a remote player from a game. Will return GGPO_ERRORCODE_PLAYER_DISCONNECTED * if you try to disconnect a player who has already been disconnected. */ -__declspec(dllexport) GGPOErrorCode __cdecl ggpo_disconnect_player(GGPOSession *, +EXPORT GGPOErrorCode __cdecl ggpo_disconnect_player(GGPOSession *, GGPOPlayerHandle player); /* @@ -481,7 +496,7 @@ __declspec(dllexport) GGPOErrorCode __cdecl ggpo_disconnect_player(GGPOSession * * you advance the gamestate by a frame, even during rollbacks. GGPO.net * may call your save_state callback before this function returns. */ -__declspec(dllexport) GGPOErrorCode __cdecl ggpo_advance_frame(GGPOSession *); +EXPORT GGPOErrorCode __cdecl ggpo_advance_frame(GGPOSession *); /* * ggpo_get_network_stats -- @@ -493,7 +508,7 @@ __declspec(dllexport) GGPOErrorCode __cdecl ggpo_advance_frame(GGPOSession *); * * stats - Out parameter to the network statistics. */ -__declspec(dllexport) GGPOErrorCode __cdecl ggpo_get_network_stats(GGPOSession *, +EXPORT GGPOErrorCode __cdecl ggpo_get_network_stats(GGPOSession *, GGPOPlayerHandle player, GGPONetworkStats *stats); @@ -509,7 +524,7 @@ __declspec(dllexport) GGPOErrorCode __cdecl ggpo_get_network_stats(GGPOSession * * * timeout - The time in milliseconds to wait before disconnecting a peer. */ -__declspec(dllexport) GGPOErrorCode __cdecl ggpo_set_disconnect_timeout(GGPOSession *, +EXPORT GGPOErrorCode __cdecl ggpo_set_disconnect_timeout(GGPOSession *, int timeout); /* @@ -521,7 +536,7 @@ __declspec(dllexport) GGPOErrorCode __cdecl ggpo_set_disconnect_timeout(GGPOSess * timeout - The amount of time which needs to elapse without receiving a packet * before the GGPO_EVENTCODE_NETWORK_INTERRUPTED event is sent. */ -__declspec(dllexport) GGPOErrorCode __cdecl ggpo_set_disconnect_notify_start(GGPOSession *, +EXPORT GGPOErrorCode __cdecl ggpo_set_disconnect_notify_start(GGPOSession *, int timeout); /* @@ -532,7 +547,7 @@ __declspec(dllexport) GGPOErrorCode __cdecl ggpo_set_disconnect_notify_start(GGP * variable is set to 1. This will change in future versions of the * SDK. */ -__declspec(dllexport) void __cdecl ggpo_log(GGPOSession *, +EXPORT void __cdecl ggpo_log(GGPOSession *, const char *fmt, ...); /* * ggpo_logv -- @@ -540,7 +555,7 @@ __declspec(dllexport) void __cdecl ggpo_log(GGPOSession *, * A varargs compatible version of ggpo_log. See ggpo_log for * more details. */ -__declspec(dllexport) void __cdecl ggpo_logv(GGPOSession *, +EXPORT void __cdecl ggpo_logv(GGPOSession *, const char *fmt, va_list args); diff --git a/src/lib/ggpo/backends/p2p.cpp b/src/lib/ggpo/backends/p2p.cpp index bd0279e6..523520c7 100644 --- a/src/lib/ggpo/backends/p2p.cpp +++ b/src/lib/ggpo/backends/p2p.cpp @@ -161,7 +161,7 @@ Peer2PeerBackend::DoPoll(int timeout) } // XXX: this is obviously a farce... if (timeout) { - Sleep(1); + Platform::SleepMS(1); } } } diff --git a/src/lib/ggpo/backends/synctest.cpp b/src/lib/ggpo/backends/synctest.cpp index 3f981a4d..73e0b4c3 100644 --- a/src/lib/ggpo/backends/synctest.cpp +++ b/src/lib/ggpo/backends/synctest.cpp @@ -167,7 +167,9 @@ SyncTestBackend::RaiseSyncError(const char *fmt, ...) va_end(args); puts(buf); +#ifdef _WIN32 OutputDebugStringA(buf); +#endif EndLog(); DebugBreak(); } @@ -187,7 +189,7 @@ SyncTestBackend::BeginLog(int saving) EndLog(); char filename[MAX_PATH]; - CreateDirectoryA("synclogs", NULL); + Platform::CreateDirectory("synclogs", NULL); sprintf(filename, "synclogs\\%s-%04d-%s.log", saving ? "state" : "log", _sync.GetFrameCount(), diff --git a/src/lib/ggpo/main.cpp b/src/lib/ggpo/main.cpp index 70576d20..8d3882cb 100644 --- a/src/lib/ggpo/main.cpp +++ b/src/lib/ggpo/main.cpp @@ -11,12 +11,18 @@ #include "backends/spectator.h" #include "ggponet.h" +#ifdef _WIN32 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { srand(Platform::GetCurrentTimeMS() + Platform::GetProcessID()); return TRUE; } +#else +static void __attribute__((constructor)) DllMain() { + srand(Platform::GetCurrentTimeMS() + Platform::GetProcessID()); +} +#endif void ggpo_log(GGPOSession *ggpo, const char *fmt, ...) diff --git a/src/lib/ggpo/network/udp.cpp b/src/lib/ggpo/network/udp.cpp index 47fd76ec..581c4741 100644 --- a/src/lib/ggpo/network/udp.cpp +++ b/src/lib/ggpo/network/udp.cpp @@ -15,10 +15,13 @@ CreateSocket(int bind_port, int retries) sockaddr_in sin; int port; int optval = 1; + struct linger loptval; + loptval.l_onoff = 0; + loptval.l_linger = 0; s = socket(AF_INET, SOCK_DGRAM, 0); setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&optval, sizeof optval); - setsockopt(s, SOL_SOCKET, SO_DONTLINGER, (const char *)&optval, sizeof optval); + setsockopt(s, SOL_SOCKET, SO_LINGER, (const char *)&loptval, sizeof loptval); // non-blocking... u_long iMode = 1; @@ -70,8 +73,12 @@ Udp::SendTo(char *buffer, int len, int flags, struct sockaddr *dst, int destlen) int res = sendto(_socket, buffer, len, flags, dst, destlen); if (res == SOCKET_ERROR) { +#ifdef _WIN32 DWORD err = WSAGetLastError(); DWORD e2 = WSAENOTSOCK; +#else + int err = 1; +#endif Log("unknown error in sendto (erro: %d wsaerr: %d).\n", res, err); ASSERT(FALSE && "Unknown error in sendto"); } @@ -83,7 +90,11 @@ Udp::OnLoopPoll(void *cookie) { uint8 recv_buf[MAX_UDP_PACKET_SIZE]; sockaddr_in recv_addr; +#ifdef _WIN32 int recv_addr_len; +#else + socklen_t recv_addr_len; +#endif for (;;) { recv_addr_len = sizeof(recv_addr); @@ -92,7 +103,11 @@ Udp::OnLoopPoll(void *cookie) // TODO: handle len == 0... indicates a disconnect. if (len == -1) { +#ifdef _WIN32 int error = WSAGetLastError(); +#else + int error = 1; +#endif if (error != WSAEWOULDBLOCK) { Log("recvfrom WSAGetLastError returned %d (%x).\n", error, error); } diff --git a/src/lib/ggpo/network/udp_proto.cpp b/src/lib/ggpo/network/udp_proto.cpp index 20412834..a20a80b6 100644 --- a/src/lib/ggpo/network/udp_proto.cpp +++ b/src/lib/ggpo/network/udp_proto.cpp @@ -296,8 +296,13 @@ UdpProtocol::HandlesMsg(sockaddr_in &from, if (!_udp) { return false; } +#ifdef _WIN32 return _peer_addr.sin_addr.S_un.S_addr == from.sin_addr.S_un.S_addr && _peer_addr.sin_port == from.sin_port; +#else + return _peer_addr.sin_addr.s_addr == from.sin_addr.s_addr && + _peer_addr.sin_port == from.sin_port; +#endif } void diff --git a/src/lib/ggpo/network/udp_proto.h b/src/lib/ggpo/network/udp_proto.h index 09de3e3f..545439cd 100644 --- a/src/lib/ggpo/network/udp_proto.h +++ b/src/lib/ggpo/network/udp_proto.h @@ -53,7 +53,7 @@ class UdpProtocol : public IPollSink } network_interrupted; } u; - UdpProtocol::Event(Type t = Unknown) : type(t) { } + Event(Type t = Unknown) : type(t) { } }; public: diff --git a/src/lib/ggpo/pevents.cpp b/src/lib/ggpo/pevents.cpp new file mode 100644 index 00000000..4691fd74 --- /dev/null +++ b/src/lib/ggpo/pevents.cpp @@ -0,0 +1,603 @@ +/* + * WIN32 Events for POSIX + * Author: Mahmoud Al-Qudsi + * Copyright (C) 2011 - 2019 by NeoSmart Technologies + * This code is released under the terms of the MIT License + */ + +#ifndef _WIN32 + +#include "pevents.h" +#include +#include +#include +#include +#ifdef WFMO +#include +#include +#endif + +namespace neosmart { +#ifdef WFMO + // Each call to WaitForMultipleObjects initializes a neosmart_wfmo_t object which tracks + // the progress of the caller's multi-object wait and dispatches responses accordingly. + // One neosmart_wfmo_t struct is shared for all events in a single WFMO call + struct neosmart_wfmo_t_ { + pthread_mutex_t Mutex; + pthread_cond_t CVariable; + int RefCount; + union { + int FiredEvent; // WFSO + int EventsLeft; // WFMO + } Status; + bool WaitAll; + bool StillWaiting; + + void Destroy() { + pthread_mutex_destroy(&Mutex); + pthread_cond_destroy(&CVariable); + } + }; + typedef neosmart_wfmo_t_ *neosmart_wfmo_t; + + // A neosmart_wfmo_info_t object is registered with each event waited on in a WFMO + // This reference to neosmart_wfmo_t_ is how the event knows whom to notify when triggered + struct neosmart_wfmo_info_t_ { + neosmart_wfmo_t Waiter; + int WaitIndex; + }; + typedef neosmart_wfmo_info_t_ *neosmart_wfmo_info_t; +#endif // WFMO + + // The basic event structure, passed to the caller as an opaque pointer when creating events + struct neosmart_event_t_ { + pthread_cond_t CVariable; + pthread_mutex_t Mutex; + bool AutoReset; + bool State; +#ifdef WFMO + std::deque RegisteredWaits; +#endif + }; + +#ifdef WFMO + bool RemoveExpiredWaitHelper(neosmart_wfmo_info_t_ wait) { + int result = pthread_mutex_trylock(&wait.Waiter->Mutex); + + if (result == EBUSY) { + return false; + } + + assert(result == 0); + + if (wait.Waiter->StillWaiting == false) { + --wait.Waiter->RefCount; + assert(wait.Waiter->RefCount >= 0); + bool destroy = wait.Waiter->RefCount == 0; + result = pthread_mutex_unlock(&wait.Waiter->Mutex); + assert(result == 0); + if (destroy) { + wait.Waiter->Destroy(); + delete wait.Waiter; + } + + return true; + } + + result = pthread_mutex_unlock(&wait.Waiter->Mutex); + assert(result == 0); + + return false; + } +#endif // WFMO + + neosmart_event_t CreateEvent(bool manualReset, bool initialState) { + neosmart_event_t event = new neosmart_event_t_; + + int result = pthread_cond_init(&event->CVariable, 0); + assert(result == 0); + + result = pthread_mutex_init(&event->Mutex, 0); + assert(result == 0); + + event->State = false; + event->AutoReset = !manualReset; + + if (initialState) { + result = SetEvent(event); + assert(result == 0); + } + + return event; + } + + int UnlockedWaitForEvent(neosmart_event_t event, uint64_t milliseconds) { + int result = 0; + if (!event->State) { + // Zero-timeout event state check optimization + if (milliseconds == 0) { + return WAIT_TIMEOUT; + } + + timespec ts; + if (milliseconds != (uint64_t)-1) { + timeval tv; + gettimeofday(&tv, NULL); + + uint64_t nanoseconds = ((uint64_t)tv.tv_sec) * 1000 * 1000 * 1000 + + milliseconds * 1000 * 1000 + ((uint64_t)tv.tv_usec) * 1000; + + ts.tv_sec = nanoseconds / 1000 / 1000 / 1000; + ts.tv_nsec = (nanoseconds - ((uint64_t)ts.tv_sec) * 1000 * 1000 * 1000); + } + + do { + // Regardless of whether it's an auto-reset or manual-reset event: + // wait to obtain the event, then lock anyone else out + if (milliseconds != (uint64_t)-1) { + result = pthread_cond_timedwait(&event->CVariable, &event->Mutex, &ts); + } else { + result = pthread_cond_wait(&event->CVariable, &event->Mutex); + } + } while (result == 0 && !event->State); + + if (result == 0 && event->AutoReset) { + // We've only accquired the event if the wait succeeded + event->State = false; + } + } else if (event->AutoReset) { + // It's an auto-reset event that's currently available; + // we need to stop anyone else from using it + result = 0; + event->State = false; + } + // Else we're trying to obtain a manual reset event with a signaled state; + // don't do anything + + return result; + } + + int WaitForEvent(neosmart_event_t event, uint64_t milliseconds) { + int tempResult; + if (milliseconds == 0) { + tempResult = pthread_mutex_trylock(&event->Mutex); + if (tempResult == EBUSY) { + return WAIT_TIMEOUT; + } + } else { + tempResult = pthread_mutex_lock(&event->Mutex); + } + + assert(tempResult == 0); + + int result = UnlockedWaitForEvent(event, milliseconds); + + tempResult = pthread_mutex_unlock(&event->Mutex); + assert(tempResult == 0); + + return result; + } + +#ifdef WFMO + int WaitForMultipleEvents(neosmart_event_t *events, int count, bool waitAll, + uint64_t milliseconds) { + int unused; + return WaitForMultipleEvents(events, count, waitAll, milliseconds, unused); + } + + int WaitForMultipleEvents(neosmart_event_t *events, int count, bool waitAll, + uint64_t milliseconds, int &waitIndex) { + neosmart_wfmo_t wfmo = new neosmart_wfmo_t_; + + int result = 0; + int tempResult = pthread_mutex_init(&wfmo->Mutex, 0); + assert(tempResult == 0); + + tempResult = pthread_cond_init(&wfmo->CVariable, 0); + assert(tempResult == 0); + + neosmart_wfmo_info_t_ waitInfo; + waitInfo.Waiter = wfmo; + waitInfo.WaitIndex = -1; + + wfmo->WaitAll = waitAll; + wfmo->StillWaiting = true; + wfmo->RefCount = 1; + + if (waitAll) { + wfmo->Status.EventsLeft = count; + } else { + wfmo->Status.FiredEvent = -1; + } + + tempResult = pthread_mutex_lock(&wfmo->Mutex); + assert(tempResult == 0); + + bool done = false; + waitIndex = -1; + + for (int i = 0; i < count; ++i) { + waitInfo.WaitIndex = i; + + // Must not release lock until RegisteredWait is potentially added + tempResult = pthread_mutex_lock(&events[i]->Mutex); + assert(tempResult == 0); + + // Before adding this wait to the list of registered waits, let's clean up old, expired + // waits while we have the event lock anyway + events[i]->RegisteredWaits.erase(std::remove_if(events[i]->RegisteredWaits.begin(), + events[i]->RegisteredWaits.end(), + RemoveExpiredWaitHelper), + events[i]->RegisteredWaits.end()); + + if (UnlockedWaitForEvent(events[i], 0) == 0) { + tempResult = pthread_mutex_unlock(&events[i]->Mutex); + assert(tempResult == 0); + + if (waitAll) { + --wfmo->Status.EventsLeft; + assert(wfmo->Status.EventsLeft >= 0); + } else { + wfmo->Status.FiredEvent = i; + waitIndex = i; + done = true; + break; + } + } else { + events[i]->RegisteredWaits.push_back(waitInfo); + ++wfmo->RefCount; + + tempResult = pthread_mutex_unlock(&events[i]->Mutex); + assert(tempResult == 0); + } + } + + // We set the `done` flag above in case of WaitAny and at least one event was set. + // But we need to check again here if we were doing a WaitAll or else we'll incorrectly + // return WAIT_TIMEOUT. + if (waitAll && wfmo->Status.EventsLeft == 0) { + done = true; + } + + timespec ts; + if (!done) { + if (milliseconds == 0) { + result = WAIT_TIMEOUT; + done = true; + } else if (milliseconds != (uint64_t)-1) { + timeval tv; + gettimeofday(&tv, NULL); + + uint64_t nanoseconds = ((uint64_t)tv.tv_sec) * 1000 * 1000 * 1000 + + milliseconds * 1000 * 1000 + ((uint64_t)tv.tv_usec) * 1000; + + ts.tv_sec = nanoseconds / 1000 / 1000 / 1000; + ts.tv_nsec = (nanoseconds - ((uint64_t)ts.tv_sec) * 1000 * 1000 * 1000); + } + } + + while (!done) { + // One (or more) of the events we're monitoring has been triggered? + + // If we're waiting for all events, assume we're done and check if there's an event that + // hasn't fired But if we're waiting for just one event, assume we're not done until we + // find a fired event + done = (waitAll && wfmo->Status.EventsLeft == 0) || + (!waitAll && wfmo->Status.FiredEvent != -1); + + if (!done) { + if (milliseconds != (uint64_t)-1) { + result = pthread_cond_timedwait(&wfmo->CVariable, &wfmo->Mutex, &ts); + } else { + result = pthread_cond_wait(&wfmo->CVariable, &wfmo->Mutex); + } + + if (result != 0) { + break; + } + } + } + + waitIndex = wfmo->Status.FiredEvent; + wfmo->StillWaiting = false; + + --wfmo->RefCount; + assert(wfmo->RefCount >= 0); + bool destroy = wfmo->RefCount == 0; + tempResult = pthread_mutex_unlock(&wfmo->Mutex); + assert(tempResult == 0); + if (destroy) { + wfmo->Destroy(); + delete wfmo; + } + + return result; + } +#endif // WFMO + + int DestroyEvent(neosmart_event_t event) { + int result = 0; + +#ifdef WFMO + result = pthread_mutex_lock(&event->Mutex); + assert(result == 0); + event->RegisteredWaits.erase(std::remove_if(event->RegisteredWaits.begin(), + event->RegisteredWaits.end(), + RemoveExpiredWaitHelper), + event->RegisteredWaits.end()); + result = pthread_mutex_unlock(&event->Mutex); + assert(result == 0); +#endif + + result = pthread_cond_destroy(&event->CVariable); + assert(result == 0); + + result = pthread_mutex_destroy(&event->Mutex); + assert(result == 0); + + delete event; + + return 0; + } + + int SetEvent(neosmart_event_t event) { + int result = pthread_mutex_lock(&event->Mutex); + assert(result == 0); + + event->State = true; + + // Depending on the event type, we either trigger everyone or only one + if (event->AutoReset) { +#ifdef WFMO + while (!event->RegisteredWaits.empty()) { + neosmart_wfmo_info_t i = &event->RegisteredWaits.front(); + + result = pthread_mutex_lock(&i->Waiter->Mutex); + assert(result == 0); + + --i->Waiter->RefCount; + assert(i->Waiter->RefCount >= 0); + if (!i->Waiter->StillWaiting) { + bool destroy = i->Waiter->RefCount == 0; + result = pthread_mutex_unlock(&i->Waiter->Mutex); + assert(result == 0); + if (destroy) { + i->Waiter->Destroy(); + delete i->Waiter; + } + event->RegisteredWaits.pop_front(); + continue; + } + + event->State = false; + + if (i->Waiter->WaitAll) { + --i->Waiter->Status.EventsLeft; + assert(i->Waiter->Status.EventsLeft >= 0); + // We technically should do i->Waiter->StillWaiting = Waiter->Status.EventsLeft + // != 0 but the only time it'll be equal to zero is if we're the last event, so + // no one else will be checking the StillWaiting flag. We're good to go without + // it. + } else { + i->Waiter->Status.FiredEvent = i->WaitIndex; + i->Waiter->StillWaiting = false; + } + + result = pthread_mutex_unlock(&i->Waiter->Mutex); + assert(result == 0); + + result = pthread_cond_signal(&i->Waiter->CVariable); + assert(result == 0); + + event->RegisteredWaits.pop_front(); + + result = pthread_mutex_unlock(&event->Mutex); + assert(result == 0); + + return 0; + } +#endif // WFMO + // event->State can be false if compiled with WFMO support + if (event->State) { + result = pthread_mutex_unlock(&event->Mutex); + assert(result == 0); + + result = pthread_cond_signal(&event->CVariable); + assert(result == 0); + + return 0; + } + } else { +#ifdef WFMO + for (size_t i = 0; i < event->RegisteredWaits.size(); ++i) { + neosmart_wfmo_info_t info = &event->RegisteredWaits[i]; + + result = pthread_mutex_lock(&info->Waiter->Mutex); + assert(result == 0); + + --info->Waiter->RefCount; + assert(info->Waiter->RefCount >= 0); + + if (!info->Waiter->StillWaiting) { + bool destroy = info->Waiter->RefCount == 0; + result = pthread_mutex_unlock(&info->Waiter->Mutex); + assert(result == 0); + if (destroy) { + info->Waiter->Destroy(); + delete info->Waiter; + } + continue; + } + + if (info->Waiter->WaitAll) { + --info->Waiter->Status.EventsLeft; + assert(info->Waiter->Status.EventsLeft >= 0); + // We technically should do i->Waiter->StillWaiting = Waiter->Status.EventsLeft + // != 0 but the only time it'll be equal to zero is if we're the last event, so + // no one else will be checking the StillWaiting flag. We're good to go without + // it. + } else { + info->Waiter->Status.FiredEvent = info->WaitIndex; + info->Waiter->StillWaiting = false; + } + + result = pthread_mutex_unlock(&info->Waiter->Mutex); + assert(result == 0); + + result = pthread_cond_signal(&info->Waiter->CVariable); + assert(result == 0); + } + event->RegisteredWaits.clear(); +#endif // WFMO + result = pthread_mutex_unlock(&event->Mutex); + assert(result == 0); + + result = pthread_cond_broadcast(&event->CVariable); + assert(result == 0); + } + + return 0; + } + + int ResetEvent(neosmart_event_t event) { + int result = pthread_mutex_lock(&event->Mutex); + assert(result == 0); + + event->State = false; + + result = pthread_mutex_unlock(&event->Mutex); + assert(result == 0); + + return 0; + } + +#ifdef PULSE + int PulseEvent(neosmart_event_t event) { + // This may look like it's a horribly inefficient kludge with the sole intention of reducing + // code duplication, but in reality this is what any PulseEvent() implementation must look + // like. The only overhead (function calls aside, which your compiler will likely optimize + // away, anyway), is if only WFMO auto-reset waits are active there will be overhead to + // unnecessarily obtain the event mutex for ResetEvent() after. In all other cases (being no + // pending waits, WFMO manual-reset waits, or any WFSO waits), the event mutex must first be + // released for the waiting thread to resume action prior to locking the mutex again in + // order to set the event state to unsignaled, or else the waiting threads will loop back + // into a wait (due to checks for spurious CVariable wakeups). + + int result = SetEvent(event); + assert(result == 0); + result = ResetEvent(event); + assert(result == 0); + + return 0; + } +#endif +} // namespace neosmart + +#else //_WIN32 + +#include +#include "pevents.h" + +namespace neosmart { + neosmart_event_t CreateEvent(bool manualReset, bool initialState) { + return static_cast(::CreateEvent(NULL, manualReset, initialState, NULL)); + } + + int DestroyEvent(neosmart_event_t event) { + HANDLE handle = static_cast(event); + return CloseHandle(handle) ? 0 : GetLastError(); + } + + int WaitForEvent(neosmart_event_t event, uint64_t milliseconds) { + uint32_t result = 0; + HANDLE handle = static_cast(event); + + // WaitForSingleObject(Ex) and WaitForMultipleObjects(Ex) only support 32-bit timeout + if (milliseconds == ((uint64_t)-1) || (milliseconds >> 32) == 0) { + result = WaitForSingleObject(handle, static_cast(milliseconds)); + } else { + // Cannot wait for 0xFFFFFFFF because that means infinity to WIN32 + uint32_t waitUnit = (INFINITE - 1); + uint64_t rounds = milliseconds / waitUnit; + uint32_t remainder = milliseconds % waitUnit; + + result = WaitForSingleObject(handle, remainder); + while (result == WAIT_TIMEOUT && rounds-- != 0) { + result = WaitForSingleObject(handle, waitUnit); + } + } + + if (result == WAIT_OBJECT_0 || result == WAIT_ABANDONED) { + // We must swallow WAIT_ABANDONED because there is no such equivalent on *nix + return 0; + } + + if (result == WAIT_TIMEOUT) { + return WAIT_TIMEOUT; + } + + return GetLastError(); + } + + int SetEvent(neosmart_event_t event) { + HANDLE handle = static_cast(event); + return ::SetEvent(handle) ? 0 : GetLastError(); + } + + int ResetEvent(neosmart_event_t event) { + HANDLE handle = static_cast(event); + return ::ResetEvent(handle) ? 0 : GetLastError(); + } + +#ifdef WFMO + int WaitForMultipleEvents(neosmart_event_t *events, int count, bool waitAll, + uint64_t milliseconds) { + int index = 0; + return WaitForMultipleEvents(events, count, waitAll, milliseconds, index); + } + + int WaitForMultipleEvents(neosmart_event_t *events, int count, bool waitAll, + uint64_t milliseconds, int &index) { + HANDLE *handles = reinterpret_cast(events); + uint32_t result = 0; + + // WaitForSingleObject(Ex) and WaitForMultipleObjects(Ex) only support 32-bit timeout + if (milliseconds == ((uint64_t)-1) || (milliseconds >> 32) == 0) { + result = WaitForMultipleObjects(count, handles, waitAll, + static_cast(milliseconds)); + } else { + // Cannot wait for 0xFFFFFFFF because that means infinity to WIN32 + uint32_t waitUnit = (INFINITE - 1); + uint64_t rounds = milliseconds / waitUnit; + uint32_t remainder = milliseconds % waitUnit; + + uint32_t result = WaitForMultipleObjects(count, handles, waitAll, remainder); + while (result == WAIT_TIMEOUT && rounds-- != 0) { + result = WaitForMultipleObjects(count, handles, waitAll, waitUnit); + } + } + + if (result >= WAIT_OBJECT_0 && result < WAIT_OBJECT_0 + count) { + index = result - WAIT_OBJECT_0; + return 0; + } else if (result >= WAIT_ABANDONED_0 && result < WAIT_ABANDONED_0 + count) { + index = result - WAIT_ABANDONED_0; + return 0; + } + + if (result == WAIT_FAILED) { + return GetLastError(); + } + return result; + } +#endif + +#ifdef PULSE + int PulseEvent(neosmart_event_t event) { + HANDLE handle = static_cast(event); + return ::PulseEvent(handle) ? 0 : GetLastError(); + } +#endif +} // namespace neosmart + +#endif //_WIN32 \ No newline at end of file diff --git a/src/lib/ggpo/pevents.h b/src/lib/ggpo/pevents.h new file mode 100644 index 00000000..85725471 --- /dev/null +++ b/src/lib/ggpo/pevents.h @@ -0,0 +1,42 @@ +/* + * WIN32 Events for POSIX + * Author: Mahmoud Al-Qudsi + * Copyright (C) 2011 - 2019 by NeoSmart Technologies + * This code is released under the terms of the MIT License + */ + +#pragma once + +#if defined(_WIN32) && !defined(CreateEvent) +#error Must include Windows.h prior to including pevents.h! +#endif +#ifndef WAIT_TIMEOUT +#include +#define WAIT_TIMEOUT ETIMEDOUT +#endif + +#include + +#define WFMO 1 + +namespace neosmart { + // Type declarations + struct neosmart_event_t_; + typedef neosmart_event_t_ *neosmart_event_t; + + // Function declarations + neosmart_event_t CreateEvent(bool manualReset = false, bool initialState = false); + int DestroyEvent(neosmart_event_t event); + int WaitForEvent(neosmart_event_t event, uint64_t milliseconds = -1); + int SetEvent(neosmart_event_t event); + int ResetEvent(neosmart_event_t event); +#ifdef WFMO + int WaitForMultipleEvents(neosmart_event_t *events, int count, bool waitAll, + uint64_t milliseconds); + int WaitForMultipleEvents(neosmart_event_t *events, int count, bool waitAll, + uint64_t milliseconds, int &index); +#endif +#ifdef PULSE + int PulseEvent(neosmart_event_t event); +#endif +} // namespace neosmart \ No newline at end of file diff --git a/src/lib/ggpo/platform_linux.cpp b/src/lib/ggpo/platform_linux.cpp index 84f88c76..73b37e87 100644 --- a/src/lib/ggpo/platform_linux.cpp +++ b/src/lib/ggpo/platform_linux.cpp @@ -21,3 +21,6 @@ uint32 Platform::GetCurrentTimeMS() { ((current.tv_nsec - start.tv_nsec ) / 1000000) + } +void Platform::CreateDirectory(const char* pathname, const void* junk) { + mkdir(pathname, -1); +} diff --git a/src/lib/ggpo/platform_linux.h b/src/lib/ggpo/platform_linux.h index b36e0511..ac586e7e 100644 --- a/src/lib/ggpo/platform_linux.h +++ b/src/lib/ggpo/platform_linux.h @@ -13,6 +13,7 @@ #include #include #include +#include class Platform { public: // types @@ -21,7 +22,9 @@ class Platform { public: // functions static ProcessID GetProcessID() { return getpid(); } static void AssertFailed(char *msg) { } - static uint32 GetCurrentTimeMS(); + static uint32_t GetCurrentTimeMS(); + static void SleepMS(int ms) { usleep(ms * 1000); } + static void CreateDirectory(const char* pathname, const void* junk); }; #endif diff --git a/src/lib/ggpo/platform_unix.cpp b/src/lib/ggpo/platform_unix.cpp new file mode 100644 index 00000000..2c1643e3 --- /dev/null +++ b/src/lib/ggpo/platform_unix.cpp @@ -0,0 +1,29 @@ +/* ----------------------------------------------------------------------- + * GGPO.net (http://ggpo.net) - Copyright 2009 GroundStorm Studios, LLC. + * + * Use of this software is governed by the MIT license that can be found + * in the LICENSE file. + */ + +#include "platform_unix.h" + +uint32_t Platform::GetCurrentTimeMS() { + struct timeval tv; + gettimeofday(&tv, NULL); + return (tv.tv_sec * 1000) + (tv.tv_usec / 1000); +} + +void Platform::SleepMS(int milliseconds) { +#if _POSIX_C_SOURCE >= 199309L + struct timespec ts; + ts.tv_sec = milliseconds / 1000; + ts.tv_nsec = (milliseconds % 1000) * 1000000; + nanosleep(&ts, NULL); +#else + usleep(milliseconds * 1000); +#endif +} + +void Platform::CreateDirectory(const char* pathname, const void* junk) { + mkdir(pathname, -1); +} \ No newline at end of file diff --git a/src/lib/ggpo/platform_unix.h b/src/lib/ggpo/platform_unix.h new file mode 100644 index 00000000..da3dd456 --- /dev/null +++ b/src/lib/ggpo/platform_unix.h @@ -0,0 +1,59 @@ +/* ----------------------------------------------------------------------- + * GGPO.net (http://ggpo.net) - Copyright 2009 GroundStorm Studios, LLC. + * + * Use of this software is governed by the MIT license that can be found + * in the LICENSE file. + */ + +#ifndef _GGPO_UNIX_H_ +#define _GGPO_UNIX_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pevents.h" + +// #define ARRAY_SIZE(a) \ +// ((sizeof(a) / sizeof(*(a))) / \ +// static_cast(!(sizeof(a) % sizeof(*(a))))) +#define DebugBreak() (raise(SIGTRAP)) +#define ioctlsocket ioctl +#define closesocket close +#define WSAEWOULDBLOCK EWOULDBLOCK +#define INFINITE (-1) +#define WAIT_OBJECT_0 (0x00000000L) +#define FALSE (false) +#define MAX_PATH (4096) +#define INVALID_SOCKET ((SOCKET)(~0)) +#define SOCKET_ERROR (-1) + +typedef neosmart::neosmart_event_t HANDLE; +typedef uint8_t byte; +typedef int SOCKET; +typedef uint32_t DWORD; + +class Platform { +public: // types + typedef pid_t ProcessID; +public: // functions + static ProcessID GetProcessID() { return getpid(); } + static void AssertFailed(char *msg) { } + static uint32_t GetCurrentTimeMS(); + static void SleepMS(int milliseconds); + static void CreateDirectory(const char* pathname, const void* junk); +}; + +#endif diff --git a/src/lib/ggpo/platform_windows.h b/src/lib/ggpo/platform_windows.h index dd4212a2..a08f95e8 100644 --- a/src/lib/ggpo/platform_windows.h +++ b/src/lib/ggpo/platform_windows.h @@ -20,6 +20,8 @@ class Platform { static ProcessID GetProcessID() { return GetCurrentProcessId(); } static void AssertFailed(char *msg) { MessageBoxA(NULL, msg, "GGPO Assertion Failed", MB_OK | MB_ICONEXCLAMATION); } static uint32 GetCurrentTimeMS() { return timeGetTime(); } + static void SleepMS(int ms) { Sleep(ms); } + static void CreateDirectory(const char* pathname, const void* junk) { CreateDirectoryA(pathname, junk); } }; #endif diff --git a/src/lib/ggpo/poll.cpp b/src/lib/ggpo/poll.cpp index 7468d6a4..403c45b7 100644 --- a/src/lib/ggpo/poll.cpp +++ b/src/lib/ggpo/poll.cpp @@ -8,6 +8,10 @@ #include "types.h" #include "poll.h" +#ifndef _WIN32 +using namespace neosmart; +#endif + Poll::Poll(void) : _handle_count(0), _start_time(0) @@ -15,7 +19,11 @@ Poll::Poll(void) : /* * Create a dummy handle to simplify things. */ +#ifdef _WIN32 _handles[_handle_count++] = CreateEvent(NULL, true, false, NULL); +#else + _handles[_handle_count++] = CreateEvent(true, false); +#endif } void @@ -68,7 +76,11 @@ Poll::Pump(int timeout) timeout = MIN(timeout, maxwait); } +#ifdef _WIN32 res = WaitForMultipleObjects(_handle_count, _handles, false, timeout); +#else + res = WaitForMultipleEvents(_handles, _handle_count, false, timeout); +#endif if (res >= WAIT_OBJECT_0 && res < WAIT_OBJECT_0 + _handle_count) { i = res - WAIT_OBJECT_0; finished = !_handle_sinks[i].sink->OnHandlePoll(_handle_sinks[i].cookie) || finished; diff --git a/src/lib/ggpo/types.h b/src/lib/ggpo/types.h index 8881a248..6cfe66f2 100644 --- a/src/lib/ggpo/types.h +++ b/src/lib/ggpo/types.h @@ -38,6 +38,8 @@ typedef int int32; */ #if defined(_WINDOWS) # include "platform_windows.h" +#elif defined(__APPLE__) +# include "platform_unix.h" #elif defined(__GNUC__) # include "platform_linux.h" #else diff --git a/src/lib/ggpo/zconf.h b/src/lib/ggpo/zconf.h index 7374ff73..4f300fc3 100644 --- a/src/lib/ggpo/zconf.h +++ b/src/lib/ggpo/zconf.h @@ -181,8 +181,8 @@ # if defined (__BORLANDC__) # if (__BORLANDC__ >= 0x0500) && defined (WIN32) # include -# define ZEXPORT __declspec(dllexport) WINAPI -# define ZEXPORTRVA __declspec(dllexport) WINAPIV +# define ZEXPORT EXPORT WINAPI +# define ZEXPORTRVA EXPORT WINAPIV # else # if defined (_Windows) && defined (__DLL__) # define ZEXPORT _export @@ -194,7 +194,7 @@ #if defined (__BEOS__) # if defined (ZLIB_DLL) -# define ZEXTERN extern __declspec(dllexport) +# define ZEXTERN extern EXPORT # else # define ZEXTERN extern __declspec(dllimport) # endif From 86673e87b9b9040d6e119699ae6948dd8dfc9f2c Mon Sep 17 00:00:00 2001 From: Bo Qin Date: Sat, 26 Oct 2019 21:55:32 -0700 Subject: [PATCH 2/2] fix for linux build. linux build successful --- src/lib/ggpo/platform_linux.cpp | 16 ++++----------- src/lib/ggpo/platform_linux.h | 35 +++++++++++++++++++++++++++++---- src/lib/ggpo/platform_unix.h | 3 --- 3 files changed, 35 insertions(+), 19 deletions(-) diff --git a/src/lib/ggpo/platform_linux.cpp b/src/lib/ggpo/platform_linux.cpp index 73b37e87..9d088c0d 100644 --- a/src/lib/ggpo/platform_linux.cpp +++ b/src/lib/ggpo/platform_linux.cpp @@ -7,18 +7,10 @@ #include "platform_linux.h" -struct timespec start = { 0 } - -uint32 Platform::GetCurrentTimeMS() { - if (start.tv_sec == 0 && start.tv_nsec == 0) { - clock_gettime(CLOCK_MONOTONIC, &start); - return 0 - } - struct timespec current; - clock_gettime(CLOCK_MONOTONIC, ¤t); - - return ((current.tv_sec - start.tv_sec) * 1000) + - ((current.tv_nsec - start.tv_nsec ) / 1000000) + +uint32_t Platform::GetCurrentTimeMS() { + struct timeval tv; + gettimeofday(&tv, NULL); + return (tv.tv_sec * 1000) + (tv.tv_usec / 1000); } void Platform::CreateDirectory(const char* pathname, const void* junk) { diff --git a/src/lib/ggpo/platform_linux.h b/src/lib/ggpo/platform_linux.h index ac586e7e..8a847f4d 100644 --- a/src/lib/ggpo/platform_linux.h +++ b/src/lib/ggpo/platform_linux.h @@ -8,17 +8,44 @@ #ifndef _GGPO_LINUX_H_ #define _GGPO_LINUX_H_ -#include -#include +#include #include +#include +#include +#include +#include #include +#include +#include #include -#include +#include +#include +#include +#include +#include +#include + +#include "pevents.h" + +#define DebugBreak() (raise(SIGTRAP)) +#define ioctlsocket ioctl +#define closesocket close +#define WSAEWOULDBLOCK EWOULDBLOCK +#define INFINITE (-1) +#define WAIT_OBJECT_0 (0x00000000L) +#define FALSE (false) +#define MAX_PATH (4096) +#define INVALID_SOCKET ((SOCKET)(~0)) +#define SOCKET_ERROR (-1) + +typedef neosmart::neosmart_event_t HANDLE; +typedef uint8_t byte; +typedef int SOCKET; +typedef uint32_t DWORD; class Platform { public: // types typedef pid_t ProcessID; - public: // functions static ProcessID GetProcessID() { return getpid(); } static void AssertFailed(char *msg) { } diff --git a/src/lib/ggpo/platform_unix.h b/src/lib/ggpo/platform_unix.h index da3dd456..9e5e4e34 100644 --- a/src/lib/ggpo/platform_unix.h +++ b/src/lib/ggpo/platform_unix.h @@ -26,9 +26,6 @@ #include "pevents.h" -// #define ARRAY_SIZE(a) \ -// ((sizeof(a) / sizeof(*(a))) / \ -// static_cast(!(sizeof(a) % sizeof(*(a))))) #define DebugBreak() (raise(SIGTRAP)) #define ioctlsocket ioctl #define closesocket close