diff --git a/output.bin b/output.bin index 6dc40e7..066fa29 100644 Binary files a/output.bin and b/output.bin differ diff --git a/src/lua.cpp b/src/lua.cpp index 5c17d80..c923543 100644 --- a/src/lua.cpp +++ b/src/lua.cpp @@ -6,7 +6,7 @@ #include "gameobject.hh" -constexpr const char METATABLE_SCRIPT[] = R"( +constexpr const char GAMEOBJECT_SCRIPT[] = R"( return function(metatable) local get_position = metatable.get_position local set_position = metatable.set_position @@ -36,16 +36,13 @@ end static int gameobjectSetPosition(psyqo::Lua L) { auto go = L.toUserdata(1); L.getField(2, "x"); - psyqo::FixedPoint<> x(L.toNumber(3), psyqo::FixedPoint<>::RAW); - go->position.x = x; + go->position.x = L.toFixedPoint(3); L.pop(); L.getField(2, "y"); - psyqo::FixedPoint<> y(L.toNumber(3), psyqo::FixedPoint<>::RAW); - go->position.y = y; + go->position.y = L.toFixedPoint(3); L.pop(); L.getField(2, "z"); - psyqo::FixedPoint<> z(L.toNumber(3), psyqo::FixedPoint<>::RAW); - go->position.z = z; + go->position.z = L.toFixedPoint(3); L.pop(); return 0; } @@ -53,18 +50,19 @@ static int gameobjectSetPosition(psyqo::Lua L) { static int gameobjectGetPosition(psyqo::Lua L) { auto go = L.toUserdata(1); L.newTable(); - L.pushNumber(go->position.x.raw()); + L.push(go->position.x); L.setField(2, "x"); - L.pushNumber(go->position.y.raw()); + L.push(go->position.y); L.setField(2, "y"); - L.pushNumber(go->position.z.raw()); + L.push(go->position.z); L.setField(2, "z"); return 1; } void psxsplash::Lua::Init() { - // Load and run the metatable script - if (L.loadBuffer(METATABLE_SCRIPT, "buffer:metatableForAllGameObjects") == 0) { + auto L = m_state; + // Load and run the game objects script + if (L.loadBuffer(GAMEOBJECT_SCRIPT, "buffer:gameObjects") == 0) { if (L.pcall(0, 1) == 0) { // This will be our metatable L.newTable(); @@ -79,7 +77,7 @@ void psxsplash::Lua::Init() { m_metatableReference = L.ref(); if (L.pcall(1, 0) == 0) { - printf("Lua script 'metatableForAllGameObjects' executed successfully"); + printf("Lua script 'gameObjects' executed successfully"); } else { printf("Error registering Lua script: %s\n", L.optString(-1, "Unknown error")); L.clearStack(); @@ -103,6 +101,7 @@ void psxsplash::Lua::Init() { } void psxsplash::Lua::LoadLuaFile(const char* code, size_t len, int index) { + auto L = m_state; char filename[32]; snprintf(filename, sizeof(filename), "lua_asset:%d", index); if (L.loadBuffer(code, len, filename) != LUA_OK) { @@ -130,12 +129,37 @@ void psxsplash::Lua::LoadLuaFile(const char* code, size_t len, int index) { } } +void psxsplash::Lua::RegisterSceneScripts(int index) { + if (index < 0) return; + auto L = m_state; + L.newTable(); + // (1) {} + L.copy(1); + // (1) {} (2) {} + m_luaSceneScriptsReference = L.ref(); + // (1) {} + L.rawGetI(LUA_REGISTRYINDEX, m_luascriptsReference); + // (1) {} (2) scripts table + L.pushNumber(index); + // (1) {} (2) script environments table (2) index + L.getTable(-2); + // (1) {} (2) script environments table (3) script environment table for the scene + onSceneCreationStartFunctionWrapper.resolveGlobal(L); + onSceneCreationEndFunctionWrapper.resolveGlobal(L); + L.pop(3); + // empty stack +} + +// We're going to store the Lua table for the object at the address of the object, +// and the table for its methods at the address of the object + 1 byte. void psxsplash::Lua::RegisterGameObject(GameObject* go) { - L.push(go); + uint8_t* ptr = reinterpret_cast(go); + auto L = m_state; + L.push(ptr); // (1) go L.newTable(); // (1) go (2) {} - L.push(go); + L.push(ptr); // (1) go (2) {} (3) go L.setField(-2, "__cpp_ptr"); // (1) go (2) { __cpp_ptr = go } @@ -150,35 +174,39 @@ void psxsplash::Lua::RegisterGameObject(GameObject* go) { // (1) go (2) { __cpp_ptr = go + metatable } L.rawSet(LUA_REGISTRYINDEX); // empty stack - printf("GameObject registered in Lua registry: %p\n", go); + L.newTable(); + // (1) {} + L.push(ptr + 1); + // (1) {} (2) go + 1 + L.copy(1); + // (1) {} (2) go + 1 (3) {} + L.rawSet(LUA_REGISTRYINDEX); + // (1) {} + if (go->luaFileIndex != -1) { + L.rawGetI(LUA_REGISTRYINDEX, m_luascriptsReference); + // (1) {} (2) script environments table + L.rawGetI(-1, go->luaFileIndex); + // (1) {} (2) script environments table (3) script environment table for this object + onCollisionMethodWrapper.resolveGlobal(L); + onInteractMethodWrapper.resolveGlobal(L); + L.pop(2); + // (1) {} + } + L.pop(); + // empty stack + printf("GameObject registered in Lua registry: %p\n", ptr); } -void psxsplash::Lua::CallOnCollide(GameObject* self, GameObject* other) { - if (self->luaFileIndex == -1) { - return; - } - L.rawGetI(LUA_REGISTRYINDEX, m_luascriptsReference); - // (1) scripts table - L.rawGetI(-1, self->luaFileIndex); - // (1) script table (2) script environment - L.getField(-1, "onCollision"); - // (1) script table (2) script environment (3) onCollision - if (!L.isFunction(-1)) { - printf("Lua function 'onCollision' not found\n"); - L.clearStack(); - return; - } +void psxsplash::Lua::OnCollision(GameObject* self, GameObject* other) { + onCollisionMethodWrapper.callMethod(*this, self, other); +} - PushGameObject(self); - PushGameObject(other); - - if (L.pcall(2, 0) != LUA_OK) { - printf("Lua error: %s\n", L.toString(-1)); - } - L.clearStack(); +void psxsplash::Lua::OnInteract(GameObject* self) { + onInteractMethodWrapper.callMethod(*this, self); } void psxsplash::Lua::PushGameObject(GameObject* go) { + auto L = m_state; L.push(go); L.rawGet(LUA_REGISTRYINDEX); diff --git a/src/lua.h b/src/lua.h index f0d714f..38a5960 100644 --- a/src/lua.h +++ b/src/lua.h @@ -1,8 +1,11 @@ #pragma once -#include +#include #include "gameobject.hh" +#include "psyqo-lua/lua.hh" +#include "psyqo/xprintf.h" +#include "typestring.h" namespace psxsplash { @@ -19,14 +22,88 @@ class Lua { void Init(); void LoadLuaFile(const char* code, size_t len, int index); + void RegisterSceneScripts(int index); void RegisterGameObject(GameObject* go); - void CallOnCollide(GameObject* self, GameObject* other); + + void OnSceneCreationStart() { + onSceneCreationStartFunctionWrapper.callFunction(*this); + } + void OnSceneCreationEnd() { + onSceneCreationEndFunctionWrapper.callFunction(*this); + } + void OnCollision(GameObject* self, GameObject* other); + void OnInteract(GameObject* self); private: + template + struct FunctionWrapper; + template + struct FunctionWrapper> { + typedef irqus::typestring methodName; + // Needs the methods table at index 1, and the script environment table at index 3 + static void resolveGlobal(psyqo::Lua L) { + L.pushNumber(methodId); + L.getTable(3); + if (L.isFunction(-1)) { + L.push(methodName::data(), methodName::size()); + L.setTable(1); + } else { + L.pop(); + } + } + template + static void pushArgs(psxsplash::Lua& lua, Args... args) { + (push(lua, args), ...); + } + static void push(psxsplash::Lua& lua, GameObject* go) { lua.PushGameObject(go); } + template + static void callMethod(psxsplash::Lua& lua, GameObject* go, Args... args) { + auto L = lua.m_state; + uint8_t* ptr = reinterpret_cast(go); + L.push(ptr + 1); + L.rawGet(LUA_REGISTRYINDEX); + L.rawGetI(-1, methodId); + if (!L.isFunction(-1)) { + L.clearStack(); + return; + } + lua.PushGameObject(go); + pushArgs(lua, args...); + if (L.pcall(sizeof...(Args) + 1, 0) != LUA_OK) { + printf("Lua error: %s\n", L.toString(-1)); + } + L.clearStack(); + } + template + static void callFunction(psxsplash::Lua& lua, Args... args) { + auto L = lua.m_state; + L.push(methodName::data(), methodName::size()); + L.rawGetI(LUA_REGISTRYINDEX, lua.m_metatableReference); + if (!L.isFunction(-1)) { + L.clearStack(); + return; + } + pushArgs(lua, args...); + if (L.pcall(sizeof...(Args), 0) != LUA_OK) { + printf("Lua error: %s\n", L.toString(-1)); + } + L.clearStack(); + } + }; + + [[no_unique_address]] FunctionWrapper<1, typestring_is("onSceneCreationStart")> onSceneCreationStartFunctionWrapper; + [[no_unique_address]] FunctionWrapper<2, typestring_is("onSceneCreationEnd")> onSceneCreationEndFunctionWrapper; + [[no_unique_address]] FunctionWrapper<1, typestring_is("onCreate")> onCreateMethodWrapper; + [[no_unique_address]] FunctionWrapper<2, typestring_is("onCollision")> onCollisionMethodWrapper; + [[no_unique_address]] FunctionWrapper<3, typestring_is("onInteract")> onInteractMethodWrapper; void PushGameObject(GameObject* go); - psyqo::Lua L; + psyqo::Lua m_state; int m_metatableReference; int m_luascriptsReference; + int m_luaSceneScriptsReference; + + template + friend struct FunctionWrapper; }; -} // namespace psxsplash \ No newline at end of file +} // namespace psxsplash diff --git a/src/scenemanager.cpp b/src/scenemanager.cpp index 2742e7c..52bce6e 100644 --- a/src/scenemanager.cpp +++ b/src/scenemanager.cpp @@ -6,6 +6,8 @@ #include "renderer.hh" #include "splashpack.hh" +#include "lua.h" + using namespace psyqo::trig_literals; void psxsplash::SceneManager::InitializeScene(uint8_t* splashpackData) { @@ -26,12 +28,18 @@ void psxsplash::SceneManager::InitializeScene(uint8_t* splashpackData) { m_playerHeight = sceneSetup.playerHeight; - // Load Lua files + // Load Lua files - order is important here. We need + // to load the Lua files before we register the game objects, + // as the game objects may reference Lua files by index. for (int i = 0; i < m_luaFiles.size(); i++) { auto luaFile = m_luaFiles[i]; L.LoadLuaFile(luaFile->luaCode, luaFile->length, i); } + L.RegisterSceneScripts(sceneSetup.sceneLuaFileIndex); + + L.OnSceneCreationStart(); + // Register game objects for (auto object : m_gameObjects) { L.RegisterGameObject(object); @@ -39,6 +47,8 @@ void psxsplash::SceneManager::InitializeScene(uint8_t* splashpackData) { m_controls.Init(); Renderer::GetInstance().SetCamera(m_currentCamera); + + L.OnSceneCreationEnd(); } void psxsplash::SceneManager::GameTick() { @@ -56,4 +66,6 @@ void psxsplash::SceneManager::GameTick() { static_cast>(m_playerPosition.y), static_cast>(m_playerPosition.z)); m_currentCamera.SetRotation(playerRotationX, playerRotationY, playerRotationZ); + + L.OnCollision(m_gameObjects[0], m_gameObjects[1]); // Example call, replace with actual logic } \ No newline at end of file diff --git a/src/splashpack.cpp b/src/splashpack.cpp index 593f60a..c157a54 100644 --- a/src/splashpack.cpp +++ b/src/splashpack.cpp @@ -65,7 +65,7 @@ void SplashPackLoader::LoadSplashpack(uint8_t *data, SplashpackSceneSetup &setup curentPointer += sizeof(psxsplash::LuaFile); } - setup.sceneLuaFile = setup.luaFiles[header->sceneLuaFileIndex]; + setup.sceneLuaFileIndex = header->sceneLuaFileIndex; for (uint16_t i = 0; i < header->gameObjectCount; i++) { psxsplash::GameObject *go = reinterpret_cast(curentPointer); diff --git a/src/splashpack.hh b/src/splashpack.hh index 0a8c360..d32936c 100644 --- a/src/splashpack.hh +++ b/src/splashpack.hh @@ -11,7 +11,7 @@ namespace psxsplash { struct SplashpackSceneSetup { - LuaFile* sceneLuaFile; + int sceneLuaFileIndex; eastl::vector luaFiles; eastl::vector objects; eastl::vector navmeshes; diff --git a/src/typestring.h b/src/typestring.h new file mode 100644 index 0000000..694c2f9 --- /dev/null +++ b/src/typestring.h @@ -0,0 +1,275 @@ +/*~ + * Copyright (C) 2015, 2016 George Makrydakis + * + * The 'typestring' header is a single header C++ library for creating types + * to use as type parameters in template instantiations, repository available + * at https://github.com/irrequietus/typestring. Conceptually stemming from + * own implementation of the same thing (but in a more complicated manner to + * be revised) in 'clause': https://github.com/irrequietus/clause. + * + * File subject to the terms and conditions of the Mozilla Public License v 2.0. + * If a copy of the MPLv2 license text was not distributed with this file, you + * can obtain it at: http://mozilla.org/MPL/2.0/. + */ + +#ifndef IRQUS_TYPESTRING_HH_ +#define IRQUS_TYPESTRING_HH_ + +namespace irqus { + +/*~ + * @desc A class 'storing' strings into distinct, reusable compile-time types that + * can be used as type parameters in a template parameter list. + * @tprm C... : char non-type parameter pack whose ordered sequence results + * into a specific string. + * @note Could have wrapped up everything in a single class, eventually will, + * once some compilers fix their class scope lookups! I have added some + * utility functions because asides being a fun little project, it is of + * use in certain constructs related to template metaprogramming + * nonetheless. + */ +template +struct typestring final { +private: + static constexpr char const vals[sizeof...(C)+1] = { C...,'\0' }; + static constexpr unsigned int sval = sizeof...(C); +public: + + static constexpr char const * data() noexcept + { return &vals[0]; } + + static constexpr unsigned int size() noexcept + { return sval; }; + + static constexpr char const * cbegin() noexcept + { return &vals[0]; } + + static constexpr char const * cend() noexcept + { return &vals[sval]; } +}; + +template +constexpr char const typestring::vals[sizeof...(C)+1]; + +//*~ part 1: preparing the ground, because function templates are awesome. + +/*~ + * @note While it is easy to resort to constexpr strings for use in constexpr + * metaprogramming, what we want is to convert compile time string in situ + * definitions into reusable, distinct types, for use in advanced template + * metaprogramming techniques. We want such features because this kind of + * metaprogramming constitutes a pure, non-strict, untyped functional + * programming language with pattern matching where declarative semantics + * can really shine. + * + * Currently, there is no feature in C++ that offers the opportunity to + * use strings as type parameter types themselves, despite there are + * several, different library implementations. This implementation is a + * fast, short, single-header, stupid-proof solution that works with any + * C++11 compliant compiler and up, with the resulting type being easily + * reusable throughout the code. + * + * @usge Just include the header and enable -std=c++11 or -std=c++14 etc, use + * like in the following example: + * + * typestring_is("Hello!") + * + * is essentially identical to the following template instantiation: + * + * irqus::typestring<'H', 'e', 'l', 'l', 'o', '!'> + * + * By passing -DUSE_TYPESTRING= during compilation, you can + * set the maximum length of the 'typestring' from 1 to 1024 (2^0 to 2^10). + * Although all preprocessor implementations tested are capable of far + * more with this method, exceeding this limit may cause internal compiler + * errors in most, with at times rather hilarious results. + */ + +template +constexpr char tygrab(char const(&c)[M]) noexcept +{ return c[N < M ? N : M-1]; } + +//*~ part2: Function template type signatures for type deduction purposes. In +// other words, exploiting the functorial nature of parameter packs +// while mixing them with an obvious catamorphism through pattern +// matching galore (partial ordering in this case in C++ "parlance"). + +template +auto typoke(typestring) // as is... + -> typestring; + +template +auto typoke(typestring, typestring<'\0'>, typestring...) + -> typestring; + +template +auto typoke(typestring, typestring, typestring...) + -> decltype(typoke(typestring(), typestring()...)); + +template +auto typeek(typestring) + -> decltype(typoke(typestring()...)); + +template +auto tycat_(typestring, typestring, X... x) + -> decltype(tycat_(typestring(), x...)); + +template +auto tycat_(typestring) + -> typestring; + +/* + * Some people actually using this header as is asked me to include + * a typestring "cat" utility given that it is easy enough to implement. + * I have added this functionality through the template alias below. For + * the obvious implementation, nothing more to say. All T... must be + * of course, "typestrings". + */ +template +using tycat + = decltype(tycat_(T()...)); + +} /* irqus */ + + +//*~ part3: some necessary code generation using preprocessor metaprogramming! +// There is functional nature in preprocessor metaprogramming as well. + +/*~ + * @note Code generation block. Undoubtedly, the preprocessor implementations + * of both clang++ and g++ are relatively competent in producing a + * relatively adequate amount of boilerplate for implementing features + * that the language itself will probably be having as features in a few + * years. At times, like herein, the preprocessor is able to generate + * boilerplate *extremely* fast, but over a certain limit the compiler is + * incapable of compiling it. For the record, only certain versions of + * g++ where capable of going beyond 4K, so I thought of going from base + * 16 to base 2 for USE_TYPESTRING power base. For the record, it takes + * a few milliseconds to generate boilerplate for several thousands worth + * of "string" length through such an 'fmap' like procedure. + */ + +/* 2^0 = 1 */ +#define TYPESTRING1(n,x) irqus::tygrab<0x##n##0>(x) + +/* 2^1 = 2 */ +#define TYPESTRING2(n,x) irqus::tygrab<0x##n##0>(x), irqus::tygrab<0x##n##1>(x) + +/* 2^2 = 2 */ +#define TYPESTRING4(n,x) \ + irqus::tygrab<0x##n##0>(x), irqus::tygrab<0x##n##1>(x) \ + , irqus::tygrab<0x##n##2>(x), irqus::tygrab<0x##n##3>(x) + +/* 2^3 = 8 */ +#define TYPESTRING8(n,x) \ + irqus::tygrab<0x##n##0>(x), irqus::tygrab<0x##n##1>(x) \ + , irqus::tygrab<0x##n##2>(x), irqus::tygrab<0x##n##3>(x) \ + , irqus::tygrab<0x##n##4>(x), irqus::tygrab<0x##n##5>(x) \ + , irqus::tygrab<0x##n##6>(x), irqus::tygrab<0x##n##7>(x) + +/* 2^4 = 16 */ +#define TYPESTRING16(n,x) \ + irqus::tygrab<0x##n##0>(x), irqus::tygrab<0x##n##1>(x) \ + , irqus::tygrab<0x##n##2>(x), irqus::tygrab<0x##n##3>(x) \ + , irqus::tygrab<0x##n##4>(x), irqus::tygrab<0x##n##5>(x) \ + , irqus::tygrab<0x##n##6>(x), irqus::tygrab<0x##n##7>(x) \ + , irqus::tygrab<0x##n##8>(x), irqus::tygrab<0x##n##9>(x) \ + , irqus::tygrab<0x##n##A>(x), irqus::tygrab<0x##n##B>(x) \ + , irqus::tygrab<0x##n##C>(x), irqus::tygrab<0x##n##D>(x) \ + , irqus::tygrab<0x##n##E>(x), irqus::tygrab<0x##n##F>(x) + +/* 2^5 = 32 */ +#define TYPESTRING32(n,x) \ + TYPESTRING16(n##0,x),TYPESTRING16(n##1,x) + +/* 2^6 = 64 */ +#define TYPESTRING64(n,x) \ + TYPESTRING16(n##0,x), TYPESTRING16(n##1,x), TYPESTRING16(n##2,x) \ + , TYPESTRING16(n##3,x) + +/* 2^7 = 128 */ +#define TYPESTRING128(n,x) \ + TYPESTRING16(n##0,x), TYPESTRING16(n##1,x), TYPESTRING16(n##2,x) \ + , TYPESTRING16(n##3,x), TYPESTRING16(n##4,x), TYPESTRING16(n##5,x) \ + , TYPESTRING16(n##6,x), TYPESTRING16(n##7,x) + +/* 2^8 = 256 */ +#define TYPESTRING256(n,x) \ + TYPESTRING16(n##0,x), TYPESTRING16(n##1,x), TYPESTRING16(n##2,x) \ + , TYPESTRING16(n##3,x), TYPESTRING16(n##4,x), TYPESTRING16(n##5,x) \ + , TYPESTRING16(n##6,x), TYPESTRING16(n##7,x), TYPESTRING16(n##8,x) \ + , TYPESTRING16(n##9,x), TYPESTRING16(n##A,x), TYPESTRING16(n##B,x) \ + , TYPESTRING16(n##C,x), TYPESTRING16(n##D,x), TYPESTRING16(n##E,x) \ + , TYPESTRING16(n##F,x) + +/* 2^9 = 512 */ +#define TYPESTRING512(n,x) \ + TYPESTRING256(n##0,x), TYPESTRING256(n##1,x) + +/* 2^10 = 1024 */ +#define TYPESTRING1024(n,x) \ + TYPESTRING256(n##0,x), TYPESTRING256(n##1,x), TYPESTRING256(n##2,x) \ + , TYPESTRING128(n##3,x), TYPESTRING16(n##38,x), TYPESTRING16(n##39,x) \ + , TYPESTRING16(n##3A,x), TYPESTRING16(n##3B,x), TYPESTRING16(n##3C,x) \ + , TYPESTRING16(n##3D,x), TYPESTRING16(n##3E,x), TYPESTRING16(n##3F,x) + +//*~ part4 : Let's give some logic with a -DUSE_TYPESTRING flag! + +#ifdef USE_TYPESTRING +#if USE_TYPESTRING == 0 +#define typestring_is(x) \ + decltype(irqus::typeek(irqus::typestring())) +#elif USE_TYPESTRING == 1 +#define typestring_is(x) \ + decltype(irqus::typeek(irqus::typestring())) +#elif USE_TYPESTRING == 2 +#define typestring_is(x) \ + decltype(irqus::typeek(irqus::typestring())) +#elif USE_TYPESTRING == 3 +#define typestring_is(x) \ + decltype(irqus::typeek(irqus::typestring())) +#elif USE_TYPESTRING == 4 +#define typestring_is(x) \ + decltype(irqus::typeek(irqus::typestring())) +#elif USE_TYPESTRING == 5 +#define typestring_is(x) \ + decltype(irqus::typeek(irqus::typestring())) +#elif USE_TYPESTRING == 6 +#define typestring_is(x) \ + decltype(irqus::typeek(irqus::typestring())) +#elif USE_TYPESTRING == 7 +#define typestring_is(x) \ + decltype(irqus::typeek(irqus::typestring())) +#elif USE_TYPESTRING == 8 +#define typestring_is(x) \ + decltype(irqus::typeek(irqus::typestring())) +#elif USE_TYPESTRING == 9 +#define typestring_is(x) \ + decltype(irqus::typeek(irqus::typestring())) +#elif USE_TYPESTRING == 10 +#define typestring_is(x) \ + decltype(irqus::typeek(irqus::typestring())) +#elif USE_TYPESTRING > 10 + +#warning !!!: custom typestring length exceeded allowed (1024) !!! +#warning !!!: all typestrings to default maximum typestring length of 64 !!! +#warning !!!: you can use -DUSE_TYPESTRING= to set length !!! + +#define typestring_is(x) \ + decltype(irqus::typeek(irqus::typestring())) + +#elif USE_TYPESTRING < 0 + +#warning !!!: You used USE_TYPESTRING with a negative size specified !!! +#warning !!!: all typestrings to default maximum typestring length of 64 !!! +#warning !!!: you can use -DUSE_TYPESTRING= to set length !!! + +#define typestring_is(x) \ + decltype(irqus::typeek(irqus::typestring())) + +#endif +#else +#define typestring_is(x) \ + decltype(irqus::typeek(irqus::typestring())) +#endif +#endif /* IRQUS_TYPESTRING_HH_ */