From addd16721fc8a8a48c6db1b3bb7044d2e5844234 Mon Sep 17 00:00:00 2001 From: MysterD Date: Fri, 21 Jan 2022 19:13:41 -0800 Subject: [PATCH] Added headless server compile mode --- Makefile | 17 +- developer/dummy.sh | 2 + src/game/obj_behaviors.c | 2 +- src/pc/controller/controller_entry_point.c | 9 + src/pc/controller/controller_sdl1.c | 8 +- src/pc/controller/controller_sdl2.c | 8 +- src/pc/djui/djui_panel_playerlist.c | 7 +- src/pc/gfx/gfx_dummy.c | 240 +++++++++++++++++++++ src/pc/gfx/gfx_dummy.h | 10 + src/pc/network/network.c | 6 + src/pc/network/network.h | 1 + src/pc/network/packets/packet_join.c | 2 + src/pc/network/packets/packet_player.c | 6 +- src/pc/pc_main.c | 5 + src/pc/platform.c | 2 + 15 files changed, 305 insertions(+), 20 deletions(-) create mode 100644 developer/dummy.sh create mode 100644 src/pc/gfx/gfx_dummy.c create mode 100644 src/pc/gfx/gfx_dummy.h diff --git a/Makefile b/Makefile index d1552296..74d2d9b3 100644 --- a/Makefile +++ b/Makefile @@ -64,11 +64,11 @@ NO_LDIV ?= 0 # Backend selection -# Renderers: GL, GL_LEGACY, D3D11, D3D12 +# Renderers: GL, GL_LEGACY, D3D11, D3D12, DUMMY RENDER_API ?= GL -# Window managers: SDL1, SDL2, DXGI (forced if D3D11 or D3D12 in RENDER_API) +# Window managers: SDL1, SDL2, DXGI (forced if D3D11 or D3D12 in RENDER_API), DUMMY (forced if RENDER_API is DUMMY) WINDOW_API ?= SDL2 -# Audio backends: SDL1, SDL2 +# Audio backends: SDL1, SDL2, DUMMY AUDIO_API ?= SDL2 # Controller backends (can have multiple, space separated): SDL2, SDL1 CONTROLLER_API ?= SDL2 @@ -242,6 +242,17 @@ else ifeq ($(WINDOW_API),DXGI) $(error DXGI can only be used with DirectX renderers) endif + ifneq ($(WINDOW_API),DUMMY) + ifeq ($(RENDER_API),DUMMY) + $(warning Dummy renderer requires dummy window API, forcing WINDOW_API value) + WINDOW_API := DUMMY + endif + else + ifneq ($(RENDER_API),DUMMY) + $(warning Dummy window API requires dummy renderer, forcing RENDER_API value) + RENDER_API := DUMMY + endif + endif endif ################### Universal Dependencies ################### diff --git a/developer/dummy.sh b/developer/dummy.sh new file mode 100644 index 00000000..93370cd5 --- /dev/null +++ b/developer/dummy.sh @@ -0,0 +1,2 @@ +#!/bin/bash +make RENDER_API=DUMMY AUDIO_API=DUMMY CONTROLLER_API= DEBUG=1 DEVELOPMENT=1 STRICT=1 -j && ./build/us_pc/sm64.us.f3dex2e.exe --server 7777 --savepath ./build/us_pc/ diff --git a/src/game/obj_behaviors.c b/src/game/obj_behaviors.c index 72562289..98b07007 100644 --- a/src/game/obj_behaviors.c +++ b/src/game/obj_behaviors.c @@ -518,9 +518,9 @@ s32 is_point_within_radius_of_mario(f32 x, f32 y, f32 z, s32 dist) { u8 is_player_active(struct MarioState* m) { if (gNetworkType == NT_NONE && m == &gMarioStates[0]) { return TRUE; } - if (m->action == ACT_BUBBLED) { return FALSE; } struct NetworkPlayer* np = &gNetworkPlayers[m->playerIndex]; + if (np == gNetworkPlayerServer && gServerSettings.headlessServer) { return FALSE; } if (np->type != NPT_LOCAL) { if (!np->connected) { return FALSE; } if (gNetworkPlayerLocal == NULL) { return FALSE; } diff --git a/src/pc/controller/controller_entry_point.c b/src/pc/controller/controller_entry_point.c index 34ca9ca0..27e0a4b7 100644 --- a/src/pc/controller/controller_entry_point.c +++ b/src/pc/controller/controller_entry_point.c @@ -11,6 +11,15 @@ // Analog camera movement by Pathétique (github.com/vrmiguel), y0shin and Mors // Contribute or communicate bugs at github.com/vrmiguel/sm64-analog-camera +// moved these from sdl controller implementations + +int mouse_x; +int mouse_y; + +int mouse_window_buttons; +int mouse_window_x; +int mouse_window_y; + static struct ControllerAPI *controller_implementations[] = { &controller_recorded_tas, #if defined(CAPI_SDL2) || defined(CAPI_SDL1) diff --git a/src/pc/controller/controller_sdl1.c b/src/pc/controller/controller_sdl1.c index 33c935e2..a94d0d79 100644 --- a/src/pc/controller/controller_sdl1.c +++ b/src/pc/controller/controller_sdl1.c @@ -18,6 +18,7 @@ #include "controller_api.h" #include "controller_sdl.h" +#include "controller_mouse.h" #include "../configfile.h" #include "../platform.h" #include "../fs/fs.h" @@ -41,13 +42,6 @@ enum { MAX_AXES, }; -int mouse_x; -int mouse_y; - -int mouse_window_buttons; -int mouse_window_x; -int mouse_window_y; - #ifdef BETTERCAMERA extern u8 newcam_mouse; #endif diff --git a/src/pc/controller/controller_sdl2.c b/src/pc/controller/controller_sdl2.c index a06b72e1..aacfc93c 100644 --- a/src/pc/controller/controller_sdl2.c +++ b/src/pc/controller/controller_sdl2.c @@ -18,6 +18,7 @@ #include "controller_api.h" #include "controller_sdl.h" +#include "controller_mouse.h" #include "../configfile.h" #include "../platform.h" #include "../fs/fs.h" @@ -31,13 +32,6 @@ #define MAX_JOYBUTTONS 32 // arbitrary; includes virtual keys for triggers #define AXIS_THRESHOLD (30 * 256) -int mouse_x; -int mouse_y; - -int mouse_window_buttons; -int mouse_window_x; -int mouse_window_y; - #ifdef BETTERCAMERA extern u8 newcam_mouse; #endif diff --git a/src/pc/djui/djui_panel_playerlist.c b/src/pc/djui/djui_panel_playerlist.c index 345cd166..d9c2f2cc 100644 --- a/src/pc/djui/djui_panel_playerlist.c +++ b/src/pc/djui/djui_panel_playerlist.c @@ -20,7 +20,12 @@ static void playerlist_update_row(u8 i, struct NetworkPlayer* np) { if (charIndex >= CT_MAX) { charIndex = 0; } djuiImages[i]->texture = gCharacters[charIndex].hudHeadTexture; - djui_base_set_visible(&djuiRow[i]->base, np->connected); + u8 visible = np->connected; + if (np == gNetworkPlayerServer && gServerSettings.headlessServer) { + visible = false; + } + + djui_base_set_visible(&djuiRow[i]->base, visible); u8* rgb = get_player_color(np->paletteIndex, 0); djui_base_set_color(&djuiTextNames[i]->base, rgb[0], rgb[1], rgb[2], 255); diff --git a/src/pc/gfx/gfx_dummy.c b/src/pc/gfx/gfx_dummy.c new file mode 100644 index 00000000..62895156 --- /dev/null +++ b/src/pc/gfx/gfx_dummy.c @@ -0,0 +1,240 @@ +#if defined(RAPI_DUMMY) || defined(WAPI_DUMMY) + +#ifdef WIN32 +#include +#elif _POSIX_C_SOURCE >= 199309L +#include // for nanosleep +#else +#include // for usleep +#endif + +#include +#include +#include + +#include "macros.h" +#include "gfx_window_manager_api.h" +#include "gfx_rendering_api.h" + +#include "pc/pc_main.h" +#include "pc/utils/misc.h" +#include "pc/debuglog.h" + +// TODO: figure out if this shit even works +#ifdef VERSION_EU +# define FRAMERATE 25 +#else +# define FRAMERATE 30 +#endif +// time between consequtive game frames +static const f64 sFrameTime = 1.0 / ((double)FRAMERATE); +static f64 sFrameTargetTime = 0; + +static void sleep_ms(int milliseconds) { // cross-platform sleep function + // from StackOverflow user Bernardo Ramos: https://stackoverflow.com/a/28827188 +#ifdef WIN32 + Sleep(milliseconds); +#elif _POSIX_C_SOURCE >= 199309L + struct timespec ts; + ts.tv_sec = milliseconds / 1000; + ts.tv_nsec = (milliseconds % 1000) * 1000000; + nanosleep(&ts, NULL); +#else + if (milliseconds >= 1000) + sleep(milliseconds / 1000); + usleep((milliseconds % 1000) * 1000); +#endif +} + +static void gfx_dummy_wm_init(UNUSED const char *game_name) { +} + +static void gfx_dummy_wm_set_keyboard_callbacks(UNUSED kb_callback_t on_key_down, UNUSED kb_callback_t on_key_up, UNUSED void (*on_all_keys_up)(void), UNUSED void (*on_text_input)(char*)) { +} + +static void gfx_dummy_wm_set_fullscreen_changed_callback(UNUSED void (*on_fullscreen_changed)(bool is_now_fullscreen)) { +} + +static void gfx_dummy_wm_set_fullscreen(UNUSED bool enable) { +} + +static void gfx_dummy_wm_main_loop(void (*run_one_game_iter)(void)) { + while (1) { + run_one_game_iter(); + } +} + +static void gfx_dummy_wm_get_dimensions(uint32_t *width, uint32_t *height) { + *width = 320; + *height = 240; +} + +static void gfx_dummy_wm_handle_events(void) { +} + +static bool gfx_dummy_wm_start_frame(void) { + return true; +} + +static inline void sync_framerate_with_timer(void) { + f64 curTime = clock_elapsed_f64(); + if (curTime < sFrameTargetTime) { + u32 delayMs = (sFrameTargetTime - curTime) * 1000.0; + if (delayMs > 0) { + sleep_ms(delayMs); + } + } + f64 frameTime = config60Fps ? (sFrameTime / 2.0) : sFrameTime; + sFrameTargetTime += frameTime; +} + +static void gfx_dummy_wm_swap_buffers_begin(void) { + sync_framerate_with_timer(); +} + +static void gfx_dummy_wm_swap_buffers_end(void) { +} + +static double gfx_dummy_wm_get_time(void) { + return 0.0; +} + +static char* gfx_dummy_wm_get_clipboard_text(void) { + return ""; +} + +static void gfx_dummy_wm_shutdown(void) { +} + +static void gfx_dummy_wm_start_text_input(void) { +} + +static void gfx_dummy_wm_stop_text_input(void) { +} + +static void gfx_dummy_wm_set_clipboard_text(UNUSED char* text) { +} + +static void gfx_dummy_wm_set_cursor_visible(UNUSED bool visible) { +} + +static bool gfx_dummy_renderer_z_is_from_0_to_1(void) { + return false; +} + +static void gfx_dummy_renderer_unload_shader(UNUSED struct ShaderProgram *old_prg) { +} + +static void gfx_dummy_renderer_load_shader(UNUSED struct ShaderProgram *new_prg) { +} + +static struct ShaderProgram *gfx_dummy_renderer_create_and_load_new_shader(UNUSED uint32_t shader_id) { + return NULL; +} + +static struct ShaderProgram *gfx_dummy_renderer_lookup_shader(UNUSED uint32_t shader_id) { + return NULL; +} + +static void gfx_dummy_renderer_shader_get_info(UNUSED struct ShaderProgram *prg, uint8_t *num_inputs, bool used_textures[2]) { + *num_inputs = 0; + used_textures[0] = false; + used_textures[1] = false; +} + +static uint32_t gfx_dummy_renderer_new_texture(void) { + return 0; +} + +static void gfx_dummy_renderer_select_texture(UNUSED int tile, UNUSED uint32_t texture_id) { +} + +static void gfx_dummy_renderer_upload_texture(UNUSED const uint8_t *rgba32_buf, UNUSED int width, UNUSED int height) { +} + +static void gfx_dummy_renderer_set_sampler_parameters(UNUSED int tile, UNUSED bool linear_filter, UNUSED uint32_t cms, UNUSED uint32_t cmt) { +} + +static void gfx_dummy_renderer_set_depth_test(UNUSED bool depth_test) { +} + +static void gfx_dummy_renderer_set_depth_mask(UNUSED bool z_upd) { +} + +static void gfx_dummy_renderer_set_zmode_decal(UNUSED bool zmode_decal) { +} + +static void gfx_dummy_renderer_set_viewport(UNUSED int x, UNUSED int y, UNUSED int width, UNUSED int height) { +} + +static void gfx_dummy_renderer_set_scissor(UNUSED int x, UNUSED int y, UNUSED int width, UNUSED int height) { +} + +static void gfx_dummy_renderer_set_use_alpha(UNUSED bool use_alpha) { +} + +static void gfx_dummy_renderer_draw_triangles(UNUSED float buf_vbo[], UNUSED size_t buf_vbo_len, UNUSED size_t buf_vbo_num_tris) { +} + +static void gfx_dummy_renderer_init(void) { +} + +static void gfx_dummy_renderer_on_resize(void) { +} + +static void gfx_dummy_renderer_start_frame(void) { +} + +static void gfx_dummy_renderer_end_frame(void) { +} + +static void gfx_dummy_renderer_finish_render(void) { +} + +static void gfx_dummy_renderer_shutdown(void) { +} + +struct GfxWindowManagerAPI gfx_dummy_wm_api = { + gfx_dummy_wm_init, + gfx_dummy_wm_set_keyboard_callbacks, + gfx_dummy_wm_main_loop, + gfx_dummy_wm_get_dimensions, + gfx_dummy_wm_handle_events, + gfx_dummy_wm_start_frame, + gfx_dummy_wm_swap_buffers_begin, + gfx_dummy_wm_swap_buffers_end, + gfx_dummy_wm_get_time, + gfx_dummy_wm_shutdown, + gfx_dummy_wm_start_text_input, + gfx_dummy_wm_stop_text_input, + gfx_dummy_wm_get_clipboard_text, + gfx_dummy_wm_set_clipboard_text, + gfx_dummy_wm_set_cursor_visible +}; + +struct GfxRenderingAPI gfx_dummy_renderer_api = { + gfx_dummy_renderer_z_is_from_0_to_1, + gfx_dummy_renderer_unload_shader, + gfx_dummy_renderer_load_shader, + gfx_dummy_renderer_create_and_load_new_shader, + gfx_dummy_renderer_lookup_shader, + gfx_dummy_renderer_shader_get_info, + gfx_dummy_renderer_new_texture, + gfx_dummy_renderer_select_texture, + gfx_dummy_renderer_upload_texture, + gfx_dummy_renderer_set_sampler_parameters, + gfx_dummy_renderer_set_depth_test, + gfx_dummy_renderer_set_depth_mask, + gfx_dummy_renderer_set_zmode_decal, + gfx_dummy_renderer_set_viewport, + gfx_dummy_renderer_set_scissor, + gfx_dummy_renderer_set_use_alpha, + gfx_dummy_renderer_draw_triangles, + gfx_dummy_renderer_init, + gfx_dummy_renderer_on_resize, + gfx_dummy_renderer_start_frame, + gfx_dummy_renderer_end_frame, + gfx_dummy_renderer_finish_render, + gfx_dummy_renderer_shutdown +}; +#endif diff --git a/src/pc/gfx/gfx_dummy.h b/src/pc/gfx/gfx_dummy.h new file mode 100644 index 00000000..8ef215a6 --- /dev/null +++ b/src/pc/gfx/gfx_dummy.h @@ -0,0 +1,10 @@ +#ifndef GFX_DUMMY_H +#define GFX_DUMMY_H + +#include "gfx_rendering_api.h" +#include "gfx_window_manager_api.h" + +extern struct GfxRenderingAPI gfx_dummy_renderer_api; +extern struct GfxWindowManagerAPI gfx_dummy_wm_api; + +#endif diff --git a/src/pc/network/network.c b/src/pc/network/network.c index 6c12b99e..b384ceda 100644 --- a/src/pc/network/network.c +++ b/src/pc/network/network.c @@ -43,6 +43,7 @@ struct ServerSettings gServerSettings = { .shareLives = 0, .enableCheats = 0, .bubbleDeath = 1, + .headlessServer = 0, }; void network_set_system(enum NetworkSystemType nsType) { @@ -70,6 +71,11 @@ bool network_init(enum NetworkType inNetworkType) { gServerSettings.shareLives = configShareLives; gServerSettings.enableCheats = configEnableCheats; gServerSettings.bubbleDeath = configBubbleDeath; +#if defined(RAPI_DUMMY) || defined(WAPI_DUMMY) + gServerSettings.headlessServer = (inNetworkType == NT_SERVER); +#else + gServerSettings.headlessServer = 0; +#endif Cheats.EnableCheats = gServerSettings.enableCheats; // initialize the network system diff --git a/src/pc/network/network.h b/src/pc/network/network.h index 11ee7c57..d669dce5 100644 --- a/src/pc/network/network.h +++ b/src/pc/network/network.h @@ -89,6 +89,7 @@ struct ServerSettings { u8 shareLives; u8 enableCheats; u8 bubbleDeath; + u8 headlessServer; }; // Networking-specific externs diff --git a/src/pc/network/packets/packet_join.c b/src/pc/network/packets/packet_join.c index bbd2e0d1..deb434a4 100644 --- a/src/pc/network/packets/packet_join.c +++ b/src/pc/network/packets/packet_join.c @@ -90,6 +90,7 @@ void network_send_join(struct Packet* joinRequestPacket) { packet_write(&p, &gServerSettings.shareLives, sizeof(u8)); packet_write(&p, &gServerSettings.enableCheats, sizeof(u8)); packet_write(&p, &gServerSettings.bubbleDeath, sizeof(u8)); + packet_write(&p, &gServerSettings.headlessServer, sizeof(u8)); packet_write(&p, eeprom, sizeof(u8) * 512); u8 modCount = string_linked_list_count(&gRegisteredMods); @@ -152,6 +153,7 @@ void network_receive_join(struct Packet* p) { packet_read(p, &gServerSettings.shareLives, sizeof(u8)); packet_read(p, &gServerSettings.enableCheats, sizeof(u8)); packet_read(p, &gServerSettings.bubbleDeath, sizeof(u8)); + packet_read(p, &gServerSettings.headlessServer, sizeof(u8)); packet_read(p, eeprom, sizeof(u8) * 512); packet_read(p, &modCount, sizeof(u8)); diff --git a/src/pc/network/packets/packet_player.c b/src/pc/network/packets/packet_player.c index 10f56c53..4659eb61 100644 --- a/src/pc/network/packets/packet_player.c +++ b/src/pc/network/packets/packet_player.c @@ -360,5 +360,9 @@ void network_receive_player(struct Packet* p) { void network_update_player(void) { if (!network_player_any_connected()) { return; } - network_send_player(0); + + u8 localIsHeadless = (&gNetworkPlayers[0] == gNetworkPlayerServer && gServerSettings.headlessServer); + if (!localIsHeadless) { + network_send_player(0); + } } diff --git a/src/pc/pc_main.c b/src/pc/pc_main.c index fda30d3d..c7d2a980 100644 --- a/src/pc/pc_main.c +++ b/src/pc/pc_main.c @@ -22,6 +22,7 @@ #include "gfx/gfx_dxgi.h" #include "gfx/gfx_sdl.h" +#include "gfx/gfx_dummy.h" #include "audio/audio_api.h" #include "audio/audio_sdl.h" @@ -231,6 +232,8 @@ void main_func(void) { wm_api = &gfx_sdl; #elif defined(WAPI_DXGI) wm_api = &gfx_dxgi; + #elif defined(WAPI_DUMMY) + wm_api = &gfx_dummy_wm_api; #else #error No window API! #endif @@ -248,6 +251,8 @@ void main_func(void) { # else # define RAPI_NAME "OpenGL" # endif + #elif defined(RAPI_DUMMY) + rendering_api = &gfx_dummy_renderer_api; #else #error No rendering API! #endif diff --git a/src/pc/platform.c b/src/pc/platform.c index 760985ed..0e295ea9 100644 --- a/src/pc/platform.c +++ b/src/pc/platform.c @@ -162,7 +162,9 @@ static void sys_fatal_impl(const char *msg) { #else +#ifndef WAPI_DUMMY #warning "You might want to implement these functions for your platform" +#endif const char *sys_user_path(void) { return ".";