Support for player entity message commands

This commit is contained in:
Kae 2023-07-19 01:16:22 +10:00
parent 770314fd7e
commit e1645f37fc
12 changed files with 133 additions and 77 deletions

View File

@ -1,5 +1,5 @@
{
"universeScriptContexts" : { "opensb" : ["/scripts/universeClient/opensb.lua"] },
"universeScriptContexts" : { "OpenStarbound" : ["/scripts/opensb/universeclient/universeclient.lua"] },
// Disables scissoring and letterboxing on vanilla and modded warp cinematics
"warpCinematicBase" : {

View File

@ -1,4 +1,5 @@
{
"genericScriptContexts" : { "OpenStarbound" : "/scripts/opensb/player/player.lua" },
"wireConfig" : {
"innerBrightnessScale" : 20,
"firstStripeThickness" : 0.6,

View File

@ -0,0 +1,30 @@
local module = {}
modules.commands = module
local commands = {}
local function command(name, func)
commands[name] = func
end
function module.init()
for name, func in pairs(commands) do
message.setHandler("/" .. name, function(isLocal, _, ...)
if not isLocal then
return
else
return func(...)
end
end)
end
end
command("run", function(src)
local success, result = pcall(loadstring, src, "/run")
if not success then
return "^#f00;compile error: " .. result
else
local success, result = pcall(result)
return not success and "^#f00;error: " .. result or sb.printJson(result)
end
end)

View File

@ -0,0 +1,2 @@
require "/scripts/opensb/util/modules.lua"
modules("/scripts/opensb/player/", {"commands"})

View File

@ -0,0 +1,2 @@
require "/scripts/opensb/util/modules.lua"
modules("/scripts/opensb/universeclient/", {"voicemanager"})

View File

@ -4,7 +4,7 @@ local fmt = string.format
local sqrt = math.sqrt
local module = {}
submodules.voice_manager = module
modules.voice_manager = module
--constants
local INDICATOR_PATH = "/interface/voicechat/indicator/"
@ -19,52 +19,35 @@ local LINE_COLOR = {50, 210, 255, 255}
local FONT_DIRECTIVES = "?border=1;333;3337?border=1;333;3330"
local NAME_PREFIX = "^noshadow,white,set;"
local canvas
local linePaddingDrawable = {
image = BACK_INDICATOR_IMAGE,
position = {0, 0},
color = LINE_COLOR,
centered = false
}
local function getLinePadding(a, b)
linePaddingDrawable.image = BACK_INDICATOR_IMAGE .. fmt("?crop=%i;%i;%i;%i?fade=fff;1", a, 0, b, INDICATOR_SIZE[2])
linePaddingDrawable.position[1] = a
return linePaddingDrawable;
local linePaddingDrawable
do
local drawable = { image = BACK_INDICATOR_IMAGE, position = {0, 0}, color = LINE_COLOR, centered = false }
function linePaddingDrawable(a, b)
drawable.image = BACK_INDICATOR_IMAGE .. fmt("?crop=%i;%i;%i;%i?fade=fff;1", a, 0, b, INDICATOR_SIZE[2])
drawable.position[1] = a
return drawable;
end
end
local lineDrawable = {
line = {{LINE_PADDING, 24}, {10, 24}},
width = 48,
color = LINE_COLOR
}
local function drawTheLine(pos, value)
local function line(pos, value)
local width = math.floor((LINE_WIDTH * value) + 0.5)
LINE_COLOR[4] = 255 * math.min(1, sqrt(width / 350))
if width > 0 then
canvas:drawDrawable(getLinePadding(0, math.min(12, width)), pos)
canvas:drawDrawable(linePaddingDrawable(0, math.min(12, width)), pos)
if width > 12 then
lineDrawable.line[2][1] = math.min(width, LINE_WIDTH_PADDED)
canvas:drawDrawable(lineDrawable, pos)
if width > LINE_WIDTH_PADDED then
canvas:drawDrawable(getLinePadding(LINE_WIDTH_PADDED, width), pos)
canvas:drawDrawable(linePaddingDrawable(LINE_WIDTH_PADDED, width), pos)
end
end
end
end
local drawable = {
image = BACK_INDICATOR_IMAGE,
centered = false
}
local canvas
local textPositioning = {
position = {0, 0},
horizontalAnchor = "left",
verticalAnchor = "mid"
}
local drawable = { image = BACK_INDICATOR_IMAGE, centered = false }
local textPositioning = { position = {0, 0}, horizontalAnchor = "left", verticalAnchor = "mid" }
local hoveredSpeaker = nil
local hoveredSpeakerIndex = 1
@ -78,7 +61,7 @@ end
local function drawSpeakerBar(mouse, pos, speaker, i)
drawable.image = BACK_INDICATOR_IMAGE
canvas:drawDrawable(drawable, pos)
drawTheLine(pos, 1 - math.sqrt(math.min(1, math.max(0, speaker.loudness / -50))))
line(pos, 1 - sqrt(math.min(1, math.max(0, speaker.loudness / -50))))
local hovering = not speaker.isLocal and mouseOverSpeaker(mouse, pos)
textPositioning.position = {pos[1] + 49, pos[2] + 24}
@ -128,7 +111,6 @@ local function drawIndicators()
local mousePosition = canvas:mousePosition()
local basePos = {screenSize[1] - 350, 50}
-- sort it ourselves for now
local speakersRemaining, speakers = {}, {}
local hoveredSpeakerId = nil
if hoveredSpeaker then
@ -140,7 +122,6 @@ local function drawIndicators()
end
local speakerCount = 0
local now = os.clock()
for i, speaker in pairs(voice.speakers()) do
local speakerId = speaker.speakerId
speakersRemaining[speakerId] = true

View File

@ -1,8 +1,10 @@
submodules = {}
modules = setmetatable({}, {__call = function(this, path, names)
for i, name in pairs(names) do
require(path .. name .. ".lua")
end
end})
require "/scripts/universeClient/opensb/voice_manager.lua"
local submodules, type = submodules, type
local modules, type = modules, type
local function call(func, ...)
if type(func) == "function" then
return func(...)
@ -11,19 +13,19 @@ end
function init(...)
script.setUpdateDelta(1)
for i, module in pairs(submodules) do
for i, module in pairs(modules) do
call(module.init, ...)
end
end
function update(...)
for i, module in pairs(submodules) do
for i, module in pairs(modules) do
call(module.update, ...)
end
end
function uninit(...)
for i, module in pairs(submodules) do
for i, module in pairs(modules) do
call(module.uninit, ...)
end
end

View File

@ -1095,6 +1095,17 @@ LuaFunction LuaEngine::createRawFunction(lua_CFunction function) {
return LuaFunction(LuaDetail::LuaHandle(RefPtr<LuaEngine>(this), popHandle(m_state)));
}
LuaFunction LuaEngine::createFunctionFromSource(int handleIndex, char const* contents, size_t size, char const* name) {
lua_checkstack(m_state, 2);
handleError(m_state, luaL_loadbuffer(m_state, contents, size, name));
pushHandle(m_state, handleIndex);
lua_setupvalue(m_state, -2, 1);
return LuaFunction(LuaDetail::LuaHandle(RefPtr<LuaEngine>(this), popHandle(m_state)));
}
void LuaEngine::pushLuaValue(lua_State* state, LuaValue const& luaValue) {
lua_checkstack(state, 1);

View File

@ -310,6 +310,7 @@ public:
using LuaTable::contains;
using LuaTable::remove;
using LuaTable::engine;
using LuaTable::handleIndex;
// Splits the path by '.' character, so can get / set values in tables inside
// other tables. If any table in the path is not a table but is accessed as
@ -530,6 +531,8 @@ public:
LuaFunction createRawFunction(lua_CFunction func);
LuaFunction createFunctionFromSource(int handleIndex, char const* contents, size_t size, char const* name);
LuaThread createThread();
template <typename T>

View File

@ -10,6 +10,7 @@
#include "StarAiInterface.hpp"
#include "StarQuestInterface.hpp"
#include "StarStatistics.hpp"
#include "StarInterfaceLuaBindings.hpp"
namespace Star {
@ -76,11 +77,10 @@ StringList ClientCommandProcessor::handleCommand(String const& commandLine) {
String allArguments = commandLine.substr(1);
String command = allArguments.extract();
auto arguments = m_parser.tokenizeToStringList(allArguments);
StringList result;
if (auto builtinCommand = m_builtinCommands.maybe(command)) {
result.append((*builtinCommand)(arguments));
result.append((*builtinCommand)(allArguments));
} else if (auto macroCommand = m_macroCommands.maybe(command)) {
for (auto const& c : *macroCommand) {
if (c.beginsWith("/"))
@ -89,6 +89,10 @@ StringList ClientCommandProcessor::handleCommand(String const& commandLine) {
result.append(c);
}
} else {
auto player = m_universeClient->mainPlayer();
if (auto messageResult = player->receiveMessage(connectionForEntity(player->entityId()), strf("/{}", command), { allArguments }))
result.append(messageResult->isType(Json::Type::String) ? *messageResult->stringPtr() : messageResult->repr(1, true));
else
m_universeClient->sendChat(commandLine, ChatSendMode::Broadcast);
}
return result;
@ -130,7 +134,8 @@ String ClientCommandProcessor::gravity() {
return toString(m_universeClient->worldClient()->gravity(m_universeClient->mainPlayer()->position()));
}
String ClientCommandProcessor::debug(StringList const& arguments) {
String ClientCommandProcessor::debug(String const& argumentsString) {
auto arguments = m_parser.tokenizeToStringList(argumentsString);
if (!adminCommandAllowed())
return "You must be an admin to use this command.";
@ -168,7 +173,8 @@ String ClientCommandProcessor::asyncLighting() {
? "enabled" : "disabled");
}
String ClientCommandProcessor::setGravity(StringList const& arguments) {
String ClientCommandProcessor::setGravity(String const& argumentsString) {
auto arguments = m_parser.tokenizeToStringList(argumentsString);
if (!adminCommandAllowed())
return "You must be an admin to use this command.";
@ -198,7 +204,8 @@ String ClientCommandProcessor::monochromeLighting() {
return strf("Monochrome lighting {}", monochrome ? "enabled" : "disabled");
}
String ClientCommandProcessor::radioMessage(StringList const& arguments) {
String ClientCommandProcessor::radioMessage(String const& argumentsString) {
auto arguments = m_parser.tokenizeToStringList(argumentsString);
if (!adminCommandAllowed())
return "You must be an admin to use this command.";
@ -225,7 +232,8 @@ String ClientCommandProcessor::clearCinematics() {
return "Player cinematic records cleared!";
}
String ClientCommandProcessor::startQuest(StringList const& arguments) {
String ClientCommandProcessor::startQuest(String const& argumentsString) {
auto arguments = m_parser.tokenizeToStringList(argumentsString);
if (!adminCommandAllowed())
return "You must be an admin to use this command.";
@ -234,7 +242,8 @@ String ClientCommandProcessor::startQuest(StringList const& arguments) {
return "Quest started";
}
String ClientCommandProcessor::completeQuest(StringList const& arguments) {
String ClientCommandProcessor::completeQuest(String const& argumentsString) {
auto arguments = m_parser.tokenizeToStringList(argumentsString);
if (!adminCommandAllowed())
return "You must be an admin to use this command.";
@ -242,7 +251,8 @@ String ClientCommandProcessor::completeQuest(StringList const& arguments) {
return strf("Quest {} complete", arguments.at(0));
}
String ClientCommandProcessor::failQuest(StringList const& arguments) {
String ClientCommandProcessor::failQuest(String const& argumentsString) {
auto arguments = m_parser.tokenizeToStringList(argumentsString);
if (!adminCommandAllowed())
return "You must be an admin to use this command.";
@ -250,7 +260,8 @@ String ClientCommandProcessor::failQuest(StringList const& arguments) {
return strf("Quest {} failed", arguments.at(0));
}
String ClientCommandProcessor::previewNewQuest(StringList const& arguments) {
String ClientCommandProcessor::previewNewQuest(String const& argumentsString) {
auto arguments = m_parser.tokenizeToStringList(argumentsString);
if (!adminCommandAllowed())
return "You must be an admin to use this command.";
@ -259,7 +270,8 @@ String ClientCommandProcessor::previewNewQuest(StringList const& arguments) {
});
}
String ClientCommandProcessor::previewQuestComplete(StringList const& arguments) {
String ClientCommandProcessor::previewQuestComplete(String const& argumentsString) {
auto arguments = m_parser.tokenizeToStringList(argumentsString);
if (!adminCommandAllowed())
return "You must be an admin to use this command.";
@ -268,7 +280,8 @@ String ClientCommandProcessor::previewQuestComplete(StringList const& arguments)
});
}
String ClientCommandProcessor::previewQuestFailed(StringList const& arguments) {
String ClientCommandProcessor::previewQuestFailed(String const& argumentsString) {
auto arguments = m_parser.tokenizeToStringList(argumentsString);
if (!adminCommandAllowed())
return "You must be an admin to use this command.";
@ -294,7 +307,8 @@ String ClientCommandProcessor::deathCount() {
return strf("Total deaths: {}{}", deaths, deaths == 0 ? ". Well done!" : "");
}
String ClientCommandProcessor::cinema(StringList const& arguments) {
String ClientCommandProcessor::cinema(String const& argumentsString) {
auto arguments = m_parser.tokenizeToStringList(argumentsString);
if (!adminCommandAllowed())
return "You must be an admin to use this command.";
@ -326,7 +340,8 @@ String ClientCommandProcessor::resetAchievements() {
return "Unable to reset achievements";
}
String ClientCommandProcessor::statistic(StringList const& arguments) {
String ClientCommandProcessor::statistic(String const& argumentsString) {
auto arguments = m_parser.tokenizeToStringList(argumentsString);
if (!adminCommandAllowed())
return "You must be an admin to use this command.";
@ -337,7 +352,8 @@ String ClientCommandProcessor::statistic(StringList const& arguments) {
return values.join("\n");
}
String ClientCommandProcessor::giveEssentialItem(StringList const& arguments) {
String ClientCommandProcessor::giveEssentialItem(String const& argumentsString) {
auto arguments = m_parser.tokenizeToStringList(argumentsString);
if (!adminCommandAllowed())
return "You must be an admin to use this command.";
@ -354,7 +370,8 @@ String ClientCommandProcessor::giveEssentialItem(StringList const& arguments) {
}
}
String ClientCommandProcessor::makeTechAvailable(StringList const& arguments) {
String ClientCommandProcessor::makeTechAvailable(String const& argumentsString) {
auto arguments = m_parser.tokenizeToStringList(argumentsString);
if (!adminCommandAllowed())
return "You must be an admin to use this command.";
@ -365,7 +382,8 @@ String ClientCommandProcessor::makeTechAvailable(StringList const& arguments) {
return strf("Added {} to player's visible techs", arguments.at(0));
}
String ClientCommandProcessor::enableTech(StringList const& arguments) {
String ClientCommandProcessor::enableTech(String const& argumentsString) {
auto arguments = m_parser.tokenizeToStringList(argumentsString);
if (!adminCommandAllowed())
return "You must be an admin to use this command.";
@ -377,7 +395,8 @@ String ClientCommandProcessor::enableTech(StringList const& arguments) {
return strf("Player tech {} enabled", arguments.at(0));
}
String ClientCommandProcessor::upgradeShip(StringList const& arguments) {
String ClientCommandProcessor::upgradeShip(String const& argumentsString) {
auto arguments = m_parser.tokenizeToStringList(argumentsString);
if (!adminCommandAllowed())
return "You must be an admin to use this command.";

View File

@ -29,40 +29,40 @@ private:
String reload();
String whoami();
String gravity();
String debug(StringList const& arguments);
String debug(String const& argumentsString);
String boxes();
String fullbright();
String asyncLighting();
String setGravity(StringList const& arguments);
String setGravity(String const& argumentsString);
String resetGravity();
String fixedCamera();
String monochromeLighting();
String radioMessage(StringList const& arguments);
String radioMessage(String const& argumentsString);
String clearRadioMessages();
String clearCinematics();
String startQuest(StringList const& arguments);
String completeQuest(StringList const& arguments);
String failQuest(StringList const& arguments);
String previewNewQuest(StringList const& arguments);
String previewQuestComplete(StringList const& arguments);
String previewQuestFailed(StringList const& arguments);
String startQuest(String const& argumentsString);
String completeQuest(String const& argumentsString);
String failQuest(String const& argumentsString);
String previewNewQuest(String const& argumentsString);
String previewQuestComplete(String const& argumentsString);
String previewQuestFailed(String const& argumentsString);
String clearScannedObjects();
String playTime();
String deathCount();
String cinema(StringList const& arguments);
String cinema(String const& argumentsString);
String suicide();
String naked();
String resetAchievements();
String statistic(StringList const& arguments);
String giveEssentialItem(StringList const& arguments);
String makeTechAvailable(StringList const& arguments);
String enableTech(StringList const& arguments);
String upgradeShip(StringList const& arguments);
String statistic(String const& argumentsString);
String giveEssentialItem(String const& argumentsString);
String makeTechAvailable(String const& argumentsString);
String enableTech(String const& argumentsString);
String upgradeShip(String const& argumentsString);
UniverseClientPtr m_universeClient;
CinematicPtr m_cinematicOverlay;
MainInterfacePaneManager* m_paneManager;
CaseInsensitiveStringMap<function<String(StringList const&)>> m_builtinCommands;
CaseInsensitiveStringMap<function<String(String const&)>> m_builtinCommands;
StringMap<StringList> m_macroCommands;
ShellParser m_parser;
LuaBaseComponent m_scriptComponent;

View File

@ -106,6 +106,11 @@ LuaContext LuaRoot::createContext(StringList const& scriptPaths) {
}
});
newContext.set("loadstring", m_luaEngine->createFunction([newContext](String const& source, Maybe<String> const& name, Maybe<LuaValue> const& env) -> LuaFunction {
String functionName = name ? strf("loadstring: {}", name) : "loadstring";
return newContext.engine().createFunctionFromSource(newContext.handleIndex(), source.utf8Ptr(), source.utf8Size(), functionName.utf8Ptr());
}));
for (auto const& scriptPath : scriptPaths)
cache->loadContextScript(newContext, scriptPath);