Add a new Channel API for experimental plugins feature (#5141)

This commit is contained in:
Mm2PL
2024-02-03 19:12:00 +01:00
committed by GitHub
parent 7fdb3841db
commit 8e9aa87a08
14 changed files with 1069 additions and 153 deletions
+118 -21
View File
@@ -1,8 +1,12 @@
#pragma once
#ifdef CHATTERINO_HAVE_PLUGINS
# include <lua.h>
# include <QString>
# include <cassert>
# include <memory>
# include <vector>
struct lua_State;
@@ -49,6 +53,11 @@ struct CompletionList {
bool hideOthers{};
};
/**
* @includefile common/Channel.hpp
* @includefile controllers/plugins/api/ChannelRef.hpp
*/
/**
* Registers a new command called `name` which when executed will call `handler`.
*
@@ -68,27 +77,6 @@ int c2_register_command(lua_State *L);
*/
int c2_register_callback(lua_State *L);
/**
* Sends a message to `channel` with the specified text. Also executes commands.
*
* **Warning**: It is possible to trigger your own Lua command with this causing a potentially infinite loop.
*
* @lua@param channel string The name of the Twitch channel
* @lua@param text string The text to be sent
* @lua@return boolean ok
* @exposed c2.send_msg
*/
int c2_send_msg(lua_State *L);
/**
* Creates a system message (gray message) and adds it to the Twitch channel specified by `channel`.
*
* @lua@param channel string
* @lua@param text string
* @lua@return boolean ok
* @exposed c2.system_msg
*/
int c2_system_msg(lua_State *L);
/**
* Writes a message to the Chatterino log.
*
@@ -107,6 +95,115 @@ int g_print(lua_State *L);
int searcherAbsolute(lua_State *L);
int searcherRelative(lua_State *L);
// This is a fat pointer that allows us to type check values given to functions needing a userdata.
// Ensure ALL userdata given to Lua are a subclass of this! Otherwise we garbage as a pointer!
struct UserData {
enum class Type { Channel };
Type type;
bool isWeak;
};
template <UserData::Type T, typename U>
struct WeakPtrUserData : public UserData {
std::weak_ptr<U> target;
WeakPtrUserData(std::weak_ptr<U> t)
: UserData()
, target(t)
{
this->type = T;
this->isWeak = true;
}
static WeakPtrUserData<T, U> *create(lua_State *L, std::weak_ptr<U> target)
{
void *ptr = lua_newuserdata(L, sizeof(WeakPtrUserData<T, U>));
return new (ptr) WeakPtrUserData<T, U>(target);
}
static WeakPtrUserData<T, U> *from(UserData *target)
{
if (!target->isWeak)
{
return nullptr;
}
if (target->type != T)
{
return nullptr;
}
return reinterpret_cast<WeakPtrUserData<T, U> *>(target);
}
static WeakPtrUserData<T, U> *from(void *target)
{
return from(reinterpret_cast<UserData *>(target));
}
static int destroy(lua_State *L)
{
auto self = WeakPtrUserData<T, U>::from(lua_touserdata(L, -1));
// Note it is safe to only check the weakness of the pointer, as
// std::weak_ptr seems to have identical representation regardless of
// what it points to
assert(self->isWeak);
self->target.reset();
lua_pop(L, 1); // Lua deallocates the memory for full user data
return 0;
}
};
template <UserData::Type T, typename U>
struct SharedPtrUserData : public UserData {
std::shared_ptr<U> target;
SharedPtrUserData(std::shared_ptr<U> t)
: UserData()
, target(t)
{
this->type = T;
this->isWeak = false;
}
static SharedPtrUserData<T, U> *create(lua_State *L,
std::shared_ptr<U> target)
{
void *ptr = lua_newuserdata(L, sizeof(SharedPtrUserData<T, U>));
return new (ptr) SharedPtrUserData<T, U>(target);
}
static SharedPtrUserData<T, U> *from(UserData *target)
{
if (target->isWeak)
{
return nullptr;
}
if (target->type != T)
{
return nullptr;
}
return reinterpret_cast<SharedPtrUserData<T, U> *>(target);
}
static SharedPtrUserData<T, U> *from(void *target)
{
return from(reinterpret_cast<UserData *>(target));
}
static int destroy(lua_State *L)
{
auto self = SharedPtrUserData<T, U>::from(lua_touserdata(L, -1));
// Note it is safe to only check the weakness of the pointer, as
// std::shared_ptr seems to have identical representation regardless of
// what it points to
assert(!self->isWeak);
self->target.reset();
lua_pop(L, 1); // Lua deallocates the memory for full user data
return 0;
}
};
} // namespace chatterino::lua::api
#endif