Skip to content

Commit be55a0c

Browse files
committed
Fully working scripting layer using c
1 parent 67e561c commit be55a0c

23 files changed

Lines changed: 632 additions & 4 deletions

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,4 +100,5 @@ node_modules
100100
mods/
101101
randomizer/
102102
!/src/port/mods/
103-
saves/
103+
saves/
104+
mod-src/output/*.c

CMakeLists.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,20 @@ endif()
303303
# Libultraship Integration #
304304
#==============================================================================#
305305

306+
find_package(libtcc QUIET)
307+
308+
if (NOT libtcc_FOUND)
309+
FetchContent_Declare(
310+
libtcc
311+
GIT_REPOSITORY https://github.com/fwsGonzo/libtcc-cmake.git
312+
GIT_TAG master
313+
)
314+
set(LIBTCC_BUILD_STATIC TRUE CACHE BOOL "" FORCE)
315+
FetchContent_MakeAvailable(libtcc)
316+
endif()
317+
318+
target_link_libraries(${PROJECT_NAME} PRIVATE libtcc)
319+
306320
# Removes MPQ/OTR support
307321
set(EXCLUDE_MPQ_SUPPORT TRUE CACHE BOOL "")
308322
set(ENABLE_EXP_AUTO_CONFIGURE_CONTROLLERS ON CACHE BOOL "")

cmake/modules/Findlibtcc.cmake

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Findlibtcc.cmake
2+
# -------------
3+
# Finds the Tiny C Compiler (libtcc) library and header.
4+
#
5+
# Defines the following variables:
6+
# libtcc_FOUND - True if libtcc was found
7+
# libtcc_INCLUDE_DIRS - Include directories for libtcc
8+
# libtcc_LIBRARIES - Libraries to link against libtcc
9+
#
10+
# Provides the following imported target:
11+
# libtcc - The libtcc library target
12+
13+
# Search for the header
14+
find_path(libtcc_INCLUDE_DIR
15+
NAMES libtcc.h
16+
DOC "Path to libtcc include directory"
17+
)
18+
19+
# Search for the library (prioritizing 'libtcc' over 'tcc')
20+
find_library(libtcc_LIBRARY
21+
NAMES libtcc tcc
22+
DOC "Path to libtcc library"
23+
)
24+
25+
# Handle standard arguments (REQUIRED, QUIET, etc.)
26+
include(FindPackageHandleStandardArgs)
27+
# THIS MUST EXACTLY MATCH your find_package() call case
28+
find_package_handle_standard_args(libtcc
29+
REQUIRED_VARS libtcc_LIBRARY libtcc_INCLUDE_DIR
30+
)
31+
32+
# Set variables and create the imported target if found
33+
if(libtcc_FOUND)
34+
set(libtcc_INCLUDE_DIRS ${libtcc_INCLUDE_DIR})
35+
set(libtcc_LIBRARIES ${libtcc_LIBRARY})
36+
37+
if(NOT TARGET libtcc)
38+
add_library(libtcc UNKNOWN IMPORTED)
39+
set_target_properties(libtcc PROPERTIES
40+
IMPORTED_LOCATION "${libtcc_LIBRARY}"
41+
INTERFACE_INCLUDE_DIRECTORIES "${libtcc_INCLUDE_DIR}"
42+
)
43+
endif()
44+
endif()
45+
46+
# Hide these from the standard CMake GUI
47+
mark_as_advanced(libtcc_INCLUDE_DIR libtcc_LIBRARY)

mod-src/CMakeLists.txt

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
cmake_minimum_required(VERSION 3.10)
2+
project(ModTemplate)
3+
4+
# Find Python instead of tcc
5+
find_package(Python3 REQUIRED COMPONENTS Interpreter)
6+
7+
set(PARENT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/..)
8+
9+
set(INCLUDE_DIRS
10+
${CMAKE_CURRENT_SOURCE_DIR}/src
11+
${CMAKE_CURRENT_SOURCE_DIR}/include
12+
${PARENT_DIR}
13+
${PARENT_DIR}/include
14+
${PARENT_DIR}/src
15+
${PARENT_DIR}/libultraship/include
16+
${PARENT_DIR}/libultraship/include/libultraship
17+
)
18+
19+
file(GLOB_RECURSE SRC_FILES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/*")
20+
21+
set(AMALGAMATED_FILE ${CMAKE_CURRENT_SOURCE_DIR}/output/mod.c)
22+
get_filename_component(OUTPUT_DIR ${AMALGAMATED_FILE} DIRECTORY)
23+
24+
add_custom_command(
25+
OUTPUT ${AMALGAMATED_FILE}
26+
COMMAND ${CMAKE_COMMAND} -E make_directory ${OUTPUT_DIR}
27+
COMMAND Python3::Interpreter ${PARENT_DIR}/tools/merge.py
28+
--out ${AMALGAMATED_FILE}
29+
--includes ${INCLUDE_DIRS}
30+
--srcs ${SRC_FILES}
31+
DEPENDS ${SRC_FILES} ${PARENT_DIR}/tools/merge.py
32+
VERBATIM
33+
)
34+
35+
add_custom_target(generate_packaged_mod ALL DEPENDS ${AMALGAMATED_FILE})

mod-src/include/mod.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#pragma once
2+
3+
#if defined(_WIN32) || defined(__CYGWIN__)
4+
#define HM_API __declspec(dllexport)
5+
#else
6+
#define HM_API __attribute__((visibility("default")))
7+
#endif
8+
9+
#define MOD_INIT HM_API void ModInit
10+
#define MOD_EXIT HM_API void ModExit

mod-src/output/manifest.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"name": "Demo Mod",
3+
"main": "mod.c",
4+
"version": "1.0.0",
5+
"website": "https://example.com",
6+
"description": "A simple demo mod for testing purposes.",
7+
"author": "Your Name",
8+
"license": "MIT"
9+
}

mod-src/src/demo.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#include "mod.h"
2+
3+
#include "port/hooks/Events.h"
4+
#include "game/level_update.h"
5+
#include "sm64.h"
6+
#include "game/print.h"
7+
#include <stdio.h>
8+
9+
void OnFrameUpdate(IEvent* event) {
10+
gMarioState->numCoins = 99;
11+
}
12+
13+
void OnGameRenderHud(IEvent* event) {
14+
print_text_centered(160, 80, "MOD HI");
15+
}
16+
17+
MOD_INIT() {
18+
REGISTER_LISTENER(GameFrameUpdate, EVENT_PRIORITY_NORMAL, OnFrameUpdate);
19+
REGISTER_LISTENER(RenderGamePost, EVENT_PRIORITY_NORMAL, OnGameRenderHud);
20+
}

src/engine/level_script.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -840,7 +840,9 @@ struct LevelCommand *level_script_execute(struct LevelCommand *cmd) {
840840

841841
profiler_log_thread5_time(LEVEL_SCRIPT_EXECUTE);
842842
init_rcp();
843+
CALL_EVENT(RenderGamePre);
843844
render_game();
845+
CALL_EVENT(RenderGamePost);
844846
end_master_display_list();
845847
alloc_display_list(0);
846848

src/game/area.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -366,10 +366,15 @@ void render_game(void) {
366366

367367
gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, BORDER_HEIGHT, SCREEN_WIDTH,
368368
SCREEN_HEIGHT - BORDER_HEIGHT);
369-
render_hud();
369+
370+
CALL_CANCELLABLE_EVENT(RenderHud) {
371+
render_hud();
372+
}
370373

371374
gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
372-
render_text_labels();
375+
CALL_CANCELLABLE_EVENT(RenderTextLabels) {
376+
render_text_labels();
377+
}
373378
do_cutscene_handler();
374379
print_displaying_credits_entry();
375380

@@ -403,7 +408,9 @@ void render_game(void) {
403408
}
404409
}
405410
} else {
406-
render_text_labels();
411+
CALL_CANCELLABLE_EVENT(RenderTextLabels) {
412+
render_text_labels();
413+
}
407414
if (D_8032CE78 != NULL) {
408415
clear_viewport(D_8032CE78, gWarpTransFBSetColor);
409416
} else {

src/port/Engine.cpp

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <SDL2/SDL_net.h>
3030
#endif
3131

32+
#include "ship/resource/type/Json.h"
3233
#include <fast/resource/ResourceType.h>
3334
#include <ship/window/gui/Fonts.h>
3435
#include <fast/resource/factory/DisplayListFactory.h>
@@ -37,15 +38,19 @@
3738
#include <fast/resource/factory/VertexFactory.h>
3839
#include <fast/resource/factory/LightFactory.h>
3940
#include <ship/resource/factory/BlobFactory.h>
41+
#include <ship/resource/factory/JsonFactory.h>
4042
#include <ship/utils/StringHelper.h>
4143
#include <ship/resource/ResourceType.h>
4244
#include <ship/window/gui/resource/Font.h>
4345

4446
#include "importer/AssetArrayFactory.h"
4547
#include "importer/RawTextureFactory.h"
48+
#include "importer/TextFactory.h"
49+
#include "port/ui/Notification.h"
4650
#include "port/importer/GenericArrayFactory.h"
4751
#include "controller/controldeck/ControlDeck.h"
4852
#include "port/mods/utils/GfxPrint.h"
53+
#include "scripting/scripting.h"
4954

5055
#ifdef __SWITCH__
5156
#include <ship/port/switch/SwitchImpl.h>
@@ -346,6 +351,7 @@ void GameEngine::FinishInit() {
346351
DevConsole_Init();
347352
PortEnhancements_Init();
348353
ShipInit::InitAll();
354+
LoadManifest();
349355
}
350356

351357
void GameEngine::RunExtract(int argc, char* argv[]) {
@@ -799,6 +805,68 @@ void GameEngine::ScaleImGui() {
799805
previousImGuiScaleIndex = imGuiScaleIndex;
800806
}
801807

808+
void GameEngine::LoadManifest() {
809+
auto archive = Ship::Context::GetInstance()->GetResourceManager()->GetArchiveManager();
810+
auto loader = Ship::Context::GetInstance()->GetResourceManager()->GetResourceLoader();
811+
auto list = archive->GetArchives();
812+
auto init = std::make_shared<Ship::ResourceInitData>();
813+
init->Type = (uint32_t)Ship::ResourceType::Json;
814+
init->ByteOrder = Ship::Endianness::Native;
815+
init->Format = RESOURCE_FORMAT_BINARY;
816+
817+
for (auto& entry : *list) {
818+
const auto path = "manifest.json";
819+
if (!entry->HasFile(path)) {
820+
continue;
821+
}
822+
823+
auto file = entry->LoadFile(path);
824+
825+
if (file == nullptr) {
826+
continue;
827+
}
828+
829+
auto raw = loader->LoadResource(path, file, init);
830+
auto res = std::static_pointer_cast<Ship::Json>(raw);
831+
if (res == nullptr) {
832+
continue;
833+
}
834+
835+
auto json = res->Data;
836+
837+
try {
838+
auto name = json["name"].get<std::string>();
839+
auto main = json["main"].get<std::string>();
840+
auto version = json.value("version", "1.0");
841+
auto website = json.value("website", "https://github.com/HarbourMasters/Ghostship");
842+
auto description = json.value("description", "");
843+
auto author = json.value("author", "unknown");
844+
auto license = json.value("license", "MIT");
845+
auto dependencies = json.value("dependencies", std::vector<std::string>());
846+
847+
SPDLOG_INFO("Name: {}", name);
848+
SPDLOG_INFO("Version: {}", version);
849+
SPDLOG_INFO("License: {}", license);
850+
SPDLOG_INFO("Author: {}", author);
851+
852+
ScriptingLayer::Instance->Load(main, entry);
853+
Notification::Emit({ .message = "Loaded " + name, .remainingTime = 7.0f });
854+
} catch (nlohmann::json::exception& e) {
855+
SPDLOG_ERROR("Invalid manifest.json, skipping {}", entry->GetPath());
856+
Notification::Emit({ .message = "Failed to load mod, check log for details",
857+
.messageColor = ImVec4(1.0f, 0.5f, 0.5f, 1.0f),
858+
.remainingTime = 7.0f });
859+
continue;
860+
} catch (std::exception& e) {
861+
SPDLOG_ERROR("Failed to load manifest.json, skipping {}: {}", entry->GetPath(), e.what());
862+
Notification::Emit({ .message = "Failed to load mod, check log for details",
863+
.messageColor = ImVec4(1.0f, 0.5f, 0.5f, 1.0f),
864+
.remainingTime = 7.0f });
865+
continue;
866+
}
867+
}
868+
}
869+
802870
void GameEngine::Create(int argc, char* argv[]) {
803871
const auto instance = Instance = new GameEngine();
804872
instance->RunExtract(argc, argv);

0 commit comments

Comments
 (0)