Basic multiplayer with enet
parent
c27090fc53
commit
261f602afe
@ -0,0 +1,3 @@
|
|||||||
|
[submodule "thirdparty/enet"]
|
||||||
|
path = thirdparty/enet
|
||||||
|
url = https://github.com/lsalzman/enet.git
|
@ -1,6 +1,156 @@
|
|||||||
#include <winsock.h>
|
#include "types.h"
|
||||||
|
#include "sokol_time.h"
|
||||||
|
#include <enet/enet.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <inttypes.h> // int64 printing
|
||||||
|
|
||||||
|
#define TIMESTEP (1.0f / 60.0f)
|
||||||
|
|
||||||
// started in a thread from host
|
// started in a thread from host
|
||||||
void server() {
|
void server(void *data)
|
||||||
// SOCKET s = socket();
|
{
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
stm_setup();
|
||||||
|
|
||||||
|
struct GameState gs = {0};
|
||||||
|
|
||||||
|
gs.boxes[0] = (struct Box){
|
||||||
|
.body = (struct Body){
|
||||||
|
.position = (P2){.x = 0.75f, .y = 0.0}},
|
||||||
|
};
|
||||||
|
gs.boxes[0].body.old_position = gs.boxes[0].body.position;
|
||||||
|
gs.boxes[1] = (struct Box){
|
||||||
|
.body = (struct Body){
|
||||||
|
.position = (P2){.x = 0.75f, .y = 0.5f}},
|
||||||
|
};
|
||||||
|
gs.boxes[1].body.old_position = gs.boxes[1].body.position;
|
||||||
|
gs.num_boxes = 2;
|
||||||
|
|
||||||
|
if (enet_initialize() != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "An error occurred while initializing ENet.\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ENetAddress address;
|
||||||
|
ENetHost *server;
|
||||||
|
address.host = ENET_HOST_ANY;
|
||||||
|
/* Bind the server to port 1234. */
|
||||||
|
address.port = 8000;
|
||||||
|
server = enet_host_create(&address /* the address to bind the server host to */,
|
||||||
|
32 /* allow up to 32 clients and/or outgoing connections */,
|
||||||
|
2 /* allow up to 2 channels to be used, 0 and 1 */,
|
||||||
|
0 /* assume any amount of incoming bandwidth */,
|
||||||
|
0 /* assume any amount of outgoing bandwidth */);
|
||||||
|
if (server == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr,
|
||||||
|
"An error occurred while trying to create an ENet server host.\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log("Serving on port 8000...\n");
|
||||||
|
ENetEvent event;
|
||||||
|
uint64_t last_processed_time = stm_now();
|
||||||
|
float total_time = 0.0f;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
// @Speed handle enet messages and simulate gamestate in parallel, then sync... must clone gamestate for this
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int ret = enet_host_service(server, &event, 16);
|
||||||
|
if (ret == 0)
|
||||||
|
break;
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Enet host service error %d\n", ret);
|
||||||
|
}
|
||||||
|
if (ret > 0)
|
||||||
|
{
|
||||||
|
switch (event.type)
|
||||||
|
{
|
||||||
|
case ENET_EVENT_TYPE_CONNECT:
|
||||||
|
Log("A new client connected from %x:%u.\n",
|
||||||
|
event.peer->address.host,
|
||||||
|
event.peer->address.port);
|
||||||
|
|
||||||
|
int64_t player_slot = -1;
|
||||||
|
for (int i = 0; i < MAX_PLAYERS; i++)
|
||||||
|
{
|
||||||
|
if (!gs.players[i].connected)
|
||||||
|
{
|
||||||
|
player_slot = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player_slot == -1)
|
||||||
|
{
|
||||||
|
enet_peer_disconnect_now(event.peer, 69);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
event.peer->data = (void *)player_slot;
|
||||||
|
gs.players[player_slot].connected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case ENET_EVENT_TYPE_RECEIVE:
|
||||||
|
// Log("A packet of length %zu was received on channel %u.\n",
|
||||||
|
// event.packet->dataLength,
|
||||||
|
// event.channelID);
|
||||||
|
|
||||||
|
size_t length = event.packet->dataLength;
|
||||||
|
if (length != sizeof(struct ClientToServer))
|
||||||
|
{
|
||||||
|
Log("Length did not match up...\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
struct ClientToServer received = {0};
|
||||||
|
memcpy(&received, event.packet->data, length);
|
||||||
|
int64_t player_slot = (int64_t)event.peer->data;
|
||||||
|
gs.players[player_slot].input = received.input;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clean up the packet now that we're done using it. */
|
||||||
|
enet_packet_destroy(event.packet);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ENET_EVENT_TYPE_DISCONNECT:
|
||||||
|
Log("%" PRId64 " disconnected.\n", (int64_t)event.peer->data);
|
||||||
|
gs.players[(int64_t)event.peer->data].connected = false;
|
||||||
|
event.peer->data = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
total_time += (float)stm_sec(stm_diff(stm_now(), last_processed_time));
|
||||||
|
last_processed_time = stm_now();
|
||||||
|
// @Robost if can't process quick enough will be stuck being lagged behind, think of a solution for this...
|
||||||
|
while (total_time > TIMESTEP)
|
||||||
|
{
|
||||||
|
process(&gs, TIMESTEP);
|
||||||
|
total_time -= TIMESTEP;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < server->peerCount; i++)
|
||||||
|
{
|
||||||
|
// @Speed don't recreate the packet for every peer, gets expensive copying gamestate over and over again
|
||||||
|
if (server->peers[i].state != ENET_PEER_STATE_CONNECTED)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
struct ServerToClient to_send;
|
||||||
|
to_send.cur_gs = gs;
|
||||||
|
to_send.your_player = (int)(int64_t)server->peers[i].data;
|
||||||
|
ENetPacket *gamestate_packet = enet_packet_create((void *)&to_send, sizeof(struct ServerToClient), ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT);
|
||||||
|
enet_peer_send(&server->peers[i], 0, gamestate_packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enet_host_destroy(server);
|
||||||
|
enet_deinitialize();
|
||||||
}
|
}
|
@ -0,0 +1 @@
|
|||||||
|
Subproject commit b06d15457909d54cd4fc0ba58c3b257d7e913f7f
|
@ -0,0 +1,319 @@
|
|||||||
|
#if defined(SOKOL_IMPL) && !defined(SOKOL_TIME_IMPL)
|
||||||
|
#define SOKOL_TIME_IMPL
|
||||||
|
#endif
|
||||||
|
#ifndef SOKOL_TIME_INCLUDED
|
||||||
|
/*
|
||||||
|
sokol_time.h -- simple cross-platform time measurement
|
||||||
|
|
||||||
|
Project URL: https://github.com/floooh/sokol
|
||||||
|
|
||||||
|
Do this:
|
||||||
|
#define SOKOL_IMPL or
|
||||||
|
#define SOKOL_TIME_IMPL
|
||||||
|
before you include this file in *one* C or C++ file to create the
|
||||||
|
implementation.
|
||||||
|
|
||||||
|
Optionally provide the following defines with your own implementations:
|
||||||
|
SOKOL_ASSERT(c) - your own assert macro (default: assert(c))
|
||||||
|
SOKOL_TIME_API_DECL - public function declaration prefix (default: extern)
|
||||||
|
SOKOL_API_DECL - same as SOKOL_TIME_API_DECL
|
||||||
|
SOKOL_API_IMPL - public function implementation prefix (default: -)
|
||||||
|
|
||||||
|
If sokol_time.h is compiled as a DLL, define the following before
|
||||||
|
including the declaration or implementation:
|
||||||
|
|
||||||
|
SOKOL_DLL
|
||||||
|
|
||||||
|
On Windows, SOKOL_DLL will define SOKOL_TIME_API_DECL as __declspec(dllexport)
|
||||||
|
or __declspec(dllimport) as needed.
|
||||||
|
|
||||||
|
void stm_setup();
|
||||||
|
Call once before any other functions to initialize sokol_time
|
||||||
|
(this calls for instance QueryPerformanceFrequency on Windows)
|
||||||
|
|
||||||
|
uint64_t stm_now();
|
||||||
|
Get current point in time in unspecified 'ticks'. The value that
|
||||||
|
is returned has no relation to the 'wall-clock' time and is
|
||||||
|
not in a specific time unit, it is only useful to compute
|
||||||
|
time differences.
|
||||||
|
|
||||||
|
uint64_t stm_diff(uint64_t new, uint64_t old);
|
||||||
|
Computes the time difference between new and old. This will always
|
||||||
|
return a positive, non-zero value.
|
||||||
|
|
||||||
|
uint64_t stm_since(uint64_t start);
|
||||||
|
Takes the current time, and returns the elapsed time since start
|
||||||
|
(this is a shortcut for "stm_diff(stm_now(), start)")
|
||||||
|
|
||||||
|
uint64_t stm_laptime(uint64_t* last_time);
|
||||||
|
This is useful for measuring frame time and other recurring
|
||||||
|
events. It takes the current time, returns the time difference
|
||||||
|
to the value in last_time, and stores the current time in
|
||||||
|
last_time for the next call. If the value in last_time is 0,
|
||||||
|
the return value will be zero (this usually happens on the
|
||||||
|
very first call).
|
||||||
|
|
||||||
|
uint64_t stm_round_to_common_refresh_rate(uint64_t duration)
|
||||||
|
This oddly named function takes a measured frame time and
|
||||||
|
returns the closest "nearby" common display refresh rate frame duration
|
||||||
|
in ticks. If the input duration isn't close to any common display
|
||||||
|
refresh rate, the input duration will be returned unchanged as a fallback.
|
||||||
|
The main purpose of this function is to remove jitter/inaccuracies from
|
||||||
|
measured frame times, and instead use the display refresh rate as
|
||||||
|
frame duration.
|
||||||
|
NOTE: for more robust frame timing, consider using the
|
||||||
|
sokol_app.h function sapp_frame_duration()
|
||||||
|
|
||||||
|
Use the following functions to convert a duration in ticks into
|
||||||
|
useful time units:
|
||||||
|
|
||||||
|
double stm_sec(uint64_t ticks);
|
||||||
|
double stm_ms(uint64_t ticks);
|
||||||
|
double stm_us(uint64_t ticks);
|
||||||
|
double stm_ns(uint64_t ticks);
|
||||||
|
Converts a tick value into seconds, milliseconds, microseconds
|
||||||
|
or nanoseconds. Note that not all platforms will have nanosecond
|
||||||
|
or even microsecond precision.
|
||||||
|
|
||||||
|
Uses the following time measurement functions under the hood:
|
||||||
|
|
||||||
|
Windows: QueryPerformanceFrequency() / QueryPerformanceCounter()
|
||||||
|
MacOS/iOS: mach_absolute_time()
|
||||||
|
emscripten: emscripten_get_now()
|
||||||
|
Linux+others: clock_gettime(CLOCK_MONOTONIC)
|
||||||
|
|
||||||
|
zlib/libpng license
|
||||||
|
|
||||||
|
Copyright (c) 2018 Andre Weissflog
|
||||||
|
|
||||||
|
This software is provided 'as-is', without any express or implied warranty.
|
||||||
|
In no event will the authors be held liable for any damages arising from the
|
||||||
|
use of this software.
|
||||||
|
|
||||||
|
Permission is granted to anyone to use this software for any purpose,
|
||||||
|
including commercial applications, and to alter it and redistribute it
|
||||||
|
freely, subject to the following restrictions:
|
||||||
|
|
||||||
|
1. The origin of this software must not be misrepresented; you must not
|
||||||
|
claim that you wrote the original software. If you use this software in a
|
||||||
|
product, an acknowledgment in the product documentation would be
|
||||||
|
appreciated but is not required.
|
||||||
|
|
||||||
|
2. Altered source versions must be plainly marked as such, and must not
|
||||||
|
be misrepresented as being the original software.
|
||||||
|
|
||||||
|
3. This notice may not be removed or altered from any source
|
||||||
|
distribution.
|
||||||
|
*/
|
||||||
|
#define SOKOL_TIME_INCLUDED (1)
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#if defined(SOKOL_API_DECL) && !defined(SOKOL_TIME_API_DECL)
|
||||||
|
#define SOKOL_TIME_API_DECL SOKOL_API_DECL
|
||||||
|
#endif
|
||||||
|
#ifndef SOKOL_TIME_API_DECL
|
||||||
|
#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_TIME_IMPL)
|
||||||
|
#define SOKOL_TIME_API_DECL __declspec(dllexport)
|
||||||
|
#elif defined(_WIN32) && defined(SOKOL_DLL)
|
||||||
|
#define SOKOL_TIME_API_DECL __declspec(dllimport)
|
||||||
|
#else
|
||||||
|
#define SOKOL_TIME_API_DECL extern
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SOKOL_TIME_API_DECL void stm_setup(void);
|
||||||
|
SOKOL_TIME_API_DECL uint64_t stm_now(void);
|
||||||
|
SOKOL_TIME_API_DECL uint64_t stm_diff(uint64_t new_ticks, uint64_t old_ticks);
|
||||||
|
SOKOL_TIME_API_DECL uint64_t stm_since(uint64_t start_ticks);
|
||||||
|
SOKOL_TIME_API_DECL uint64_t stm_laptime(uint64_t* last_time);
|
||||||
|
SOKOL_TIME_API_DECL uint64_t stm_round_to_common_refresh_rate(uint64_t frame_ticks);
|
||||||
|
SOKOL_TIME_API_DECL double stm_sec(uint64_t ticks);
|
||||||
|
SOKOL_TIME_API_DECL double stm_ms(uint64_t ticks);
|
||||||
|
SOKOL_TIME_API_DECL double stm_us(uint64_t ticks);
|
||||||
|
SOKOL_TIME_API_DECL double stm_ns(uint64_t ticks);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
#endif // SOKOL_TIME_INCLUDED
|
||||||
|
|
||||||
|
/*-- IMPLEMENTATION ----------------------------------------------------------*/
|
||||||
|
#ifdef SOKOL_TIME_IMPL
|
||||||
|
#define SOKOL_TIME_IMPL_INCLUDED (1)
|
||||||
|
#include <string.h> /* memset */
|
||||||
|
|
||||||
|
#ifndef SOKOL_API_IMPL
|
||||||
|
#define SOKOL_API_IMPL
|
||||||
|
#endif
|
||||||
|
#ifndef SOKOL_ASSERT
|
||||||
|
#include <assert.h>
|
||||||
|
#define SOKOL_ASSERT(c) assert(c)
|
||||||
|
#endif
|
||||||
|
#ifndef _SOKOL_PRIVATE
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
#define _SOKOL_PRIVATE __attribute__((unused)) static
|
||||||
|
#else
|
||||||
|
#define _SOKOL_PRIVATE static
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#endif
|
||||||
|
#include <windows.h>
|
||||||
|
typedef struct {
|
||||||
|
uint32_t initialized;
|
||||||
|
LARGE_INTEGER freq;
|
||||||
|
LARGE_INTEGER start;
|
||||||
|
} _stm_state_t;
|
||||||
|
#elif defined(__APPLE__) && defined(__MACH__)
|
||||||
|
#include <mach/mach_time.h>
|
||||||
|
typedef struct {
|
||||||
|
uint32_t initialized;
|
||||||
|
mach_timebase_info_data_t timebase;
|
||||||
|
uint64_t start;
|
||||||
|
} _stm_state_t;
|
||||||
|
#elif defined(__EMSCRIPTEN__)
|
||||||
|
#include <emscripten/emscripten.h>
|
||||||
|
typedef struct {
|
||||||
|
uint32_t initialized;
|
||||||
|
double start;
|
||||||
|
} _stm_state_t;
|
||||||
|
#else /* anything else, this will need more care for non-Linux platforms */
|
||||||
|
#ifdef ESP8266
|
||||||
|
// On the ESP8266, clock_gettime ignores the first argument and CLOCK_MONOTONIC isn't defined
|
||||||
|
#define CLOCK_MONOTONIC 0
|
||||||
|
#endif
|
||||||
|
#include <time.h>
|
||||||
|
typedef struct {
|
||||||
|
uint32_t initialized;
|
||||||
|
uint64_t start;
|
||||||
|
} _stm_state_t;
|
||||||
|
#endif
|
||||||
|
static _stm_state_t _stm;
|
||||||
|
|
||||||
|
/* prevent 64-bit overflow when computing relative timestamp
|
||||||
|
see https://gist.github.com/jspohr/3dc4f00033d79ec5bdaf67bc46c813e3
|
||||||
|
*/
|
||||||
|
#if defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__))
|
||||||
|
_SOKOL_PRIVATE int64_t _stm_int64_muldiv(int64_t value, int64_t numer, int64_t denom) {
|
||||||
|
int64_t q = value / denom;
|
||||||
|
int64_t r = value % denom;
|
||||||
|
return q * numer + r * numer / denom;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SOKOL_API_IMPL void stm_setup(void) {
|
||||||
|
memset(&_stm, 0, sizeof(_stm));
|
||||||
|
_stm.initialized = 0xABCDABCD;
|
||||||
|
#if defined(_WIN32)
|
||||||
|
QueryPerformanceFrequency(&_stm.freq);
|
||||||
|
QueryPerformanceCounter(&_stm.start);
|
||||||
|
#elif defined(__APPLE__) && defined(__MACH__)
|
||||||
|
mach_timebase_info(&_stm.timebase);
|
||||||
|
_stm.start = mach_absolute_time();
|
||||||
|
#elif defined(__EMSCRIPTEN__)
|
||||||
|
_stm.start = emscripten_get_now();
|
||||||
|
#else
|
||||||
|
struct timespec ts;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||||
|
_stm.start = (uint64_t)ts.tv_sec*1000000000 + (uint64_t)ts.tv_nsec;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
SOKOL_API_IMPL uint64_t stm_now(void) {
|
||||||
|
SOKOL_ASSERT(_stm.initialized == 0xABCDABCD);
|
||||||
|
uint64_t now;
|
||||||
|
#if defined(_WIN32)
|
||||||
|
LARGE_INTEGER qpc_t;
|
||||||
|
QueryPerformanceCounter(&qpc_t);
|
||||||
|
now = (uint64_t) _stm_int64_muldiv(qpc_t.QuadPart - _stm.start.QuadPart, 1000000000, _stm.freq.QuadPart);
|
||||||
|
#elif defined(__APPLE__) && defined(__MACH__)
|
||||||
|
const uint64_t mach_now = mach_absolute_time() - _stm.start;
|
||||||
|
now = (uint64_t) _stm_int64_muldiv((int64_t)mach_now, (int64_t)_stm.timebase.numer, (int64_t)_stm.timebase.denom);
|
||||||
|
#elif defined(__EMSCRIPTEN__)
|
||||||
|
double js_now = emscripten_get_now() - _stm.start;
|
||||||
|
now = (uint64_t) (js_now * 1000000.0);
|
||||||
|
#else
|
||||||
|
struct timespec ts;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||||
|
now = ((uint64_t)ts.tv_sec*1000000000 + (uint64_t)ts.tv_nsec) - _stm.start;
|
||||||
|
#endif
|
||||||
|
return now;
|
||||||
|
}
|
||||||
|
|
||||||
|
SOKOL_API_IMPL uint64_t stm_diff(uint64_t new_ticks, uint64_t old_ticks) {
|
||||||
|
if (new_ticks > old_ticks) {
|
||||||
|
return new_ticks - old_ticks;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SOKOL_API_IMPL uint64_t stm_since(uint64_t start_ticks) {
|
||||||
|
return stm_diff(stm_now(), start_ticks);
|
||||||
|
}
|
||||||
|
|
||||||
|
SOKOL_API_IMPL uint64_t stm_laptime(uint64_t* last_time) {
|
||||||
|
SOKOL_ASSERT(last_time);
|
||||||
|
uint64_t dt = 0;
|
||||||
|
uint64_t now = stm_now();
|
||||||
|
if (0 != *last_time) {
|
||||||
|
dt = stm_diff(now, *last_time);
|
||||||
|
}
|
||||||
|
*last_time = now;
|
||||||
|
return dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// first number is frame duration in ns, second number is tolerance in ns,
|
||||||
|
// the resulting min/max values must not overlap!
|
||||||
|
static const uint64_t _stm_refresh_rates[][2] = {
|
||||||
|
{ 16666667, 1000000 }, // 60 Hz: 16.6667 +- 1ms
|
||||||
|
{ 13888889, 250000 }, // 72 Hz: 13.8889 +- 0.25ms
|
||||||
|
{ 13333333, 250000 }, // 75 Hz: 13.3333 +- 0.25ms
|
||||||
|
{ 11764706, 250000 }, // 85 Hz: 11.7647 +- 0.25
|
||||||
|
{ 11111111, 250000 }, // 90 Hz: 11.1111 +- 0.25ms
|
||||||
|
{ 10000000, 500000 }, // 100 Hz: 10.0000 +- 0.5ms
|
||||||
|
{ 8333333, 500000 }, // 120 Hz: 8.3333 +- 0.5ms
|
||||||
|
{ 6944445, 500000 }, // 144 Hz: 6.9445 +- 0.5ms
|
||||||
|
{ 4166667, 1000000 }, // 240 Hz: 4.1666 +- 1ms
|
||||||
|
{ 0, 0 }, // keep the last element always at zero
|
||||||
|
};
|
||||||
|
|
||||||
|
SOKOL_API_IMPL uint64_t stm_round_to_common_refresh_rate(uint64_t ticks) {
|
||||||
|
uint64_t ns;
|
||||||
|
int i = 0;
|
||||||
|
while (0 != (ns = _stm_refresh_rates[i][0])) {
|
||||||
|
uint64_t tol = _stm_refresh_rates[i][1];
|
||||||
|
if ((ticks > (ns - tol)) && (ticks < (ns + tol))) {
|
||||||
|
return ns;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
// fallthough: didn't fit into any buckets
|
||||||
|
return ticks;
|
||||||
|
}
|
||||||
|
|
||||||
|
SOKOL_API_IMPL double stm_sec(uint64_t ticks) {
|
||||||
|
return (double)ticks / 1000000000.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SOKOL_API_IMPL double stm_ms(uint64_t ticks) {
|
||||||
|
return (double)ticks / 1000000.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SOKOL_API_IMPL double stm_us(uint64_t ticks) {
|
||||||
|
return (double)ticks / 1000.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SOKOL_API_IMPL double stm_ns(uint64_t ticks) {
|
||||||
|
return (double)ticks;
|
||||||
|
}
|
||||||
|
#endif /* SOKOL_TIME_IMPL */
|
||||||
|
|
Loading…
Reference in New Issue