diff options
49 files changed, 364 insertions, 90 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 51c223f..6474e7e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,6 +26,11 @@ on: - '*' pull_request: + paths: + - 'assets/**' + - 'source/**' + - 'toolchains/**' + - 'triplets/**' branches: - '*' diff --git a/assets/opensb/interface/opensb/shaders/shaders.lua b/assets/opensb/interface/opensb/shaders/shaders.lua index f9b6a32..befbabf 100644 --- a/assets/opensb/interface/opensb/shaders/shaders.lua +++ b/assets/opensb/interface/opensb/shaders/shaders.lua @@ -184,6 +184,18 @@ local function addOption(data, i, added) end end +local function updateParameter(parameter,oname,value) + if parameter.isPasses then + for k,v in next, parameter.layers do + renderer.setPostProcessLayerPasses(v, value) + end + else + for k,v in next, parameter.effects do + renderer.setEffectParameter(v, oname, value) + end + end +end + function sliderOptionModified(option, odata) local oname = string.sub(option, optionOffset) local parameter = groups[activeGroup].parameters[oname] @@ -191,15 +203,12 @@ function sliderOptionModified(option, odata) widget.setText(fmt("%s.%s",OPTIONS_WIDGET,option.."_value"), fmt(digitRegex(parameter.delta),value)) - for k,v in next, parameter.effects do - renderer.setEffectParameter(v, oname, value) - end + updateParameter(parameter,oname,value) shaderConfig[activeGroup].parameters[oname] = value root.setConfiguration("postProcessGroups",shaderConfig) end function selectOptionPressed(button,bdata) - sb.logInfo(sb.print(bdata)) for k,v in next, bdata.buttons do if v.index ~= bdata.index then @@ -212,9 +221,7 @@ function selectOptionPressed(button,bdata) local oname = bdata.option.name local parameter = groups[activeGroup].parameters[oname] - for k,v in next, parameter.effects do - renderer.setEffectParameter(v, oname, value) - end + updateParameter(parameter,oname,value) shaderConfig[activeGroup].parameters[oname] = value root.setConfiguration("postProcessGroups",shaderConfig) end diff --git a/assets/opensb/interface/windowconfig/multiplayer.config.patch b/assets/opensb/interface/windowconfig/multiplayer.config.patch index 8bd27c1..bfd6297 100644 --- a/assets/opensb/interface/windowconfig/multiplayer.config.patch +++ b/assets/opensb/interface/windowconfig/multiplayer.config.patch @@ -4,5 +4,25 @@ "anchor" : "center", "positionLocked" : true }, + "connect": { + "position" : [34, 32] + }, + "legacyLabel" : { + "type" : "label", + "position" : [130, 34], + "hAnchor" : "left", + "value" : "FORCE LEGACY" + }, + "legacyCheckbox" : { + "type" : "button", + "pressedOffset" : [0, 0], + "position" : [120, 34], + "base" : "/interface/optionsmenu/checkboxnocheck.png", + "hover" : "/interface/optionsmenu/checkboxnocheckhover.png", + "baseImageChecked" : "/interface/optionsmenu/checkboxcheck.png", + "hoverImageChecked" : "/interface/optionsmenu/checkboxcheckhover.png", + "checkable" : true, + "checked": false + }, "password" : { "hidden" : true } -}
\ No newline at end of file +} diff --git a/assets/opensb/scripts/opensb/universeclient/loadconfig.lua b/assets/opensb/scripts/opensb/universeclient/loadconfig.lua index c0fc845..0898bc0 100644 --- a/assets/opensb/scripts/opensb/universeclient/loadconfig.lua +++ b/assets/opensb/scripts/opensb/universeclient/loadconfig.lua @@ -11,9 +11,17 @@ function module.init() local group = postProcessGroups[k] if group and v.parameters then for k2,v2 in next, group.parameters do - if v.parameters[k2] ~= nil then - for _,e in next, v2.effects do - renderer.setEffectParameter(e,k2,v.parameters[k2]) + local param = v.parameters[k2] + if param ~= nil then + if v2.isPasses then + -- this parameter controls passes + for _,l in next, v2.layers do + renderer.setPostProcessLayerPasses(l,param) + end + else + for _,e in next, v2.effects do + renderer.setEffectParameter(e,k2,param) + end end end end diff --git a/doc/lua/openstarbound/chat.md b/doc/lua/openstarbound/chat.md new file mode 100644 index 0000000..af9c96d --- /dev/null +++ b/doc/lua/openstarbound/chat.md @@ -0,0 +1,59 @@ +# Chat + +The new chat table is accessible from almost every clientside script and allows interaction with the in-game chat system. + +--- + +#### `double` chat.send(`String` message, [`String` modeName], [`bool` speak], [`Json` data])) + +Sends a new message in the chat. Optionally specifies the modeName: + +- `"Broadcast"`: Global chat. Default value. +- `"Local"`: Chat within the current planet. +- `"Party"`: Chat within the current party. + +If `speak` is false, the chat bubble will not appear above the player. +If `data` is provided, it will be sent as a JSON object with the message. This can be used to send custom data with the message. + +--- + +#### `String[]` chat.command(`String` command) + +Executes the specified command and returns a list of strings with the result. + +--- + +#### `void` chat.addMessage(`String` text, [`Json` config]) + +Adds the specified message to the chat log. The following keys are available in the `config` JSON object: + +- `String` __mode__ - The mode of the message. Can be one of the followgin: + - `"Broadcast"` + - `"Local"` + - `"Party"` + - `"Whisper"` + - `"CommandResult"` + - `"RadioMessage"` + - `"World"` +- `String` __channelName__ - The name of the channel to send the message to. +- `String` __fromNick__ - The name of the sender of the message. +- `String` __portrait__ - message portrait. +- `bool` __showPane__ - If false, the chat pane will not be triggered. + +--- + +#### `String` chat.input() + +Returns the current chat input text. + +--- + +#### `bool` chat.setInput(`String` text, [`bool` moveCursor]) + +Sets the current chat input text. If `moveCursor` is true, the cursor will be moved to the end of the text. Returns true if the input was set successfully, false otherwise. + +--- + +#### `void` chat.clear([`unsigned`] count) + +Clears the chat input text. If `count` is provided, it will clear the last `count` messages, all otherwise.
\ No newline at end of file diff --git a/source/base/StarAssets.cpp b/source/base/StarAssets.cpp index 9742f9e..b6aeab9 100644 --- a/source/base/StarAssets.cpp +++ b/source/base/StarAssets.cpp @@ -969,8 +969,9 @@ ImageConstPtr Assets::readImage(String const& path) const { auto& patchSource = pair.second; auto patchStream = patchSource->read(patchPath); if (patchPath.endsWith(".lua")) { + std::pair<AssetSource*, String> contextKey = make_pair(patchSource.get(), patchPath); luaLocker.lock(); - LuaContextPtr& context = m_patchContexts[patchPath]; + LuaContextPtr& context = m_patchContexts[contextKey]; if (!context) { context = make_shared<LuaContext>(luaEngine->createContext()); context->load(patchStream, patchPath); @@ -1042,9 +1043,10 @@ Json Assets::readJson(String const& path) const { auto& patchSource = pair.second; auto patchStream = patchSource->read(patchBasePath); if (patchBasePath.endsWith(".lua")) { + std::pair<AssetSource*, String> contextKey = make_pair(patchSource.get(), patchBasePath); RecursiveMutexLocker luaLocker(m_luaMutex); // Kae: i don't like that lock. perhaps have a LuaEngine and patch context cache per worker thread later on? - LuaContextPtr& context = m_patchContexts[patchBasePath]; + LuaContextPtr& context = m_patchContexts[contextKey]; if (!context) { context = make_shared<LuaContext>(as<LuaEngine>(m_luaEngine.get())->createContext()); context->load(patchStream, patchBasePath); diff --git a/source/base/StarAssets.hpp b/source/base/StarAssets.hpp index a8e4480..6cc8c16 100644 --- a/source/base/StarAssets.hpp +++ b/source/base/StarAssets.hpp @@ -331,7 +331,7 @@ private: // Lua RefPtr<RefCounter> m_luaEngine; // dumb but to avoid including Lua.hpp in here... - mutable StringMap<LuaContextPtr> m_patchContexts; + mutable HashMap<pair<AssetSource*, String>, LuaContextPtr> m_patchContexts; mutable RecursiveMutex m_luaMutex; // Paths of all used asset sources, in load order. diff --git a/source/client/StarClientApplication.cpp b/source/client/StarClientApplication.cpp index 3c7ec8d..f79226e 100644 --- a/source/client/StarClientApplication.cpp +++ b/source/client/StarClientApplication.cpp @@ -73,7 +73,8 @@ Json const AdditionalDefaultConfiguration = Json::parseJson(R"JSON( "title" : { "multiPlayerAddress" : "", "multiPlayerPort" : "", - "multiPlayerAccount" : "" + "multiPlayerAccount" : "", + "multiPlayerForceLegacy" : false }, "bindings" : { @@ -151,7 +152,7 @@ void ClientApplication::startup(StringList const& cmdLineArgs) { RootLoader rootLoader({AdditionalAssetsSettings, AdditionalDefaultConfiguration, String("starbound.log"), LogLevel::Info, false, String("starbound.config")}); m_root = rootLoader.initOrDie(cmdLineArgs).first; - Logger::info("Client Version {} ({}) Source ID: {} Protocol: {}", StarVersionString, StarArchitectureString, StarSourceIdentifierString, StarProtocolVersion); + Logger::info("OpenStarbound Client v{} for v{} ({}) Source ID: {} Protocol: {}", OpenStarVersionString, StarVersionString, StarArchitectureString, StarSourceIdentifierString, StarProtocolVersion); } void ClientApplication::shutdown() { @@ -492,6 +493,7 @@ void ClientApplication::renderReload() { // define post process layers and optionally assign them to groups m_postProcessLayers.clear(); + m_labelledPostProcessLayers.clear(); auto postProcessLayers = assets->json("/client.config:postProcessLayers").toArray(); for (auto& layer : postProcessLayers) { auto effects = jsonToStringList(layer.getArray("effects")); @@ -502,18 +504,28 @@ void ClientApplication::renderReload() { if (gname) { group = &m_postProcessGroups.get(gname.value()); } + // I'd think a string map for all of these would be better, but the order does matter here, and does make sense to depend on mod priority, so... + // guess a string map of indices works + // I tried pointers but for whatever reason the behaviour was highly inconsistent and only worked after reload... + auto label = layer.optString("name"); + if (label) { + m_labelledPostProcessLayers.add(label.value(),m_postProcessLayers.count()); + } m_postProcessLayers.append(PostProcessLayer{ std::move(effects), (unsigned)layer.getUInt("passes", 1), group }); } loadEffectConfig("interface"); } -void ClientApplication::setPostProcessGroupEnabled(String group, bool enabled, Maybe<bool> save) { +void ClientApplication::setPostProcessLayerPasses(String const& layer, unsigned const& passes) { + m_postProcessLayers.at(m_labelledPostProcessLayers.get(layer)).passes = passes; +} +void ClientApplication::setPostProcessGroupEnabled(String const& group, bool const& enabled, Maybe<bool> const& save) { m_postProcessGroups.get(group).enabled = enabled; if (save && save.value()) m_root->configuration()->setPath(strf("{}.{}.enabled", postProcessGroupsRoot, group),enabled); } -bool ClientApplication::postProcessGroupEnabled(String group) { +bool ClientApplication::postProcessGroupEnabled(String const& group) { return m_postProcessGroups.get(group).enabled; } @@ -627,6 +639,7 @@ void ClientApplication::changeState(MainAppState newState) { m_titleScreen->setMultiPlayerAddress(toString(address->address())); m_titleScreen->setMultiPlayerPort(toString(address->port())); m_titleScreen->setMultiPlayerAccount(configuration->getPath("title.multiPlayerAccount").toString()); + m_titleScreen->setMultiPlayerForceLegacy(configuration->getPath("title.multiPlayerForceLegacy").optBool().value(false)); m_titleScreen->goToMultiPlayerSelectCharacter(false); } else { m_titleScreen->goToMultiPlayerSelectCharacter(true); @@ -635,6 +648,7 @@ void ClientApplication::changeState(MainAppState newState) { m_titleScreen->setMultiPlayerAddress(configuration->getPath("title.multiPlayerAddress").toString()); m_titleScreen->setMultiPlayerPort(configuration->getPath("title.multiPlayerPort").toString()); m_titleScreen->setMultiPlayerAccount(configuration->getPath("title.multiPlayerAccount").toString()); + m_titleScreen->setMultiPlayerForceLegacy(configuration->getPath("title.multiPlayerForceLegacy").optBool().value(false)); } } @@ -699,7 +713,7 @@ void ClientApplication::changeState(MainAppState newState) { bool allowAssetsMismatch = m_root->configuration()->get("allowAssetsMismatch").toBool(); if (auto errorMessage = m_universeClient->connect(UniverseConnection(std::move(packetSocket)), allowAssetsMismatch, - multiPlayerConnection.account, multiPlayerConnection.password)) { + multiPlayerConnection.account, multiPlayerConnection.password, multiPlayerConnection.forceLegacy)) { setError(*errorMessage); return; } @@ -871,13 +885,15 @@ void ClientApplication::updateTitle(float dt) { m_pendingMultiPlayerConnection = PendingMultiPlayerConnection{ address.right(), m_titleScreen->multiPlayerAccount(), - m_titleScreen->multiPlayerPassword() + m_titleScreen->multiPlayerPassword(), + m_titleScreen->multiPlayerForceLegacy() }; auto configuration = m_root->configuration(); configuration->setPath("title.multiPlayerAddress", m_titleScreen->multiPlayerAddress()); configuration->setPath("title.multiPlayerPort", m_titleScreen->multiPlayerPort()); configuration->setPath("title.multiPlayerAccount", m_titleScreen->multiPlayerAccount()); + configuration->setPath("title.multiPlayerForceLegacy", m_titleScreen->multiPlayerForceLegacy()); changeState(MainAppState::MultiPlayer); } diff --git a/source/client/StarClientApplication.hpp b/source/client/StarClientApplication.hpp index 86b50a3..8824ea0 100644 --- a/source/client/StarClientApplication.hpp +++ b/source/client/StarClientApplication.hpp @@ -19,8 +19,9 @@ STAR_CLASS(Voice); class ClientApplication : public Application { public: - void setPostProcessGroupEnabled(String group, bool enabled, Maybe<bool> save); - bool postProcessGroupEnabled(String group); + void setPostProcessLayerPasses(String const& layer, unsigned const& passes); + void setPostProcessGroupEnabled(String const& group, bool const& enabled, Maybe<bool> const& save); + bool postProcessGroupEnabled(String const& group); Json postProcessGroups(); protected: @@ -56,6 +57,7 @@ private: Variant<P2PNetworkingPeerId, HostAddressWithPort> server; String account; String password; + bool forceLegacy; }; struct PostProcessGroup { @@ -116,6 +118,7 @@ private: StringMap<PostProcessGroup> m_postProcessGroups; List<PostProcessLayer> m_postProcessLayers; + StringMap<size_t> m_labelledPostProcessLayers; // Valid if main app state == SinglePlayer UniverseServerPtr m_universeServer; diff --git a/source/client/StarRenderingLuaBindings.cpp b/source/client/StarRenderingLuaBindings.cpp index 39dc1a1..4493f71 100644 --- a/source/client/StarRenderingLuaBindings.cpp +++ b/source/client/StarRenderingLuaBindings.cpp @@ -37,6 +37,9 @@ LuaCallbacks LuaBindings::makeRenderingCallbacks(ClientApplication* app) { auto renderer = app->renderer(); return renderer->getEffectScriptableParameter(effectName, effectParameter); }); + + // not saved; should be loaded by Lua again + callbacks.registerCallbackWithSignature<void, String, unsigned>("setPostProcessLayerPasses", bind(mem_fn(&ClientApplication::setPostProcessLayerPasses), app, _1, _2)); return callbacks; } diff --git a/source/core/StarDataStream.cpp b/source/core/StarDataStream.cpp index 7097d63..2688123 100644 --- a/source/core/StarDataStream.cpp +++ b/source/core/StarDataStream.cpp @@ -6,7 +6,7 @@ namespace Star { -unsigned const CurrentStreamVersion = 3; +unsigned const CurrentStreamVersion = 6; // update OpenProtocolVersion too! DataStream::DataStream() : m_byteOrder(ByteOrder::BigEndian), diff --git a/source/core/StarLua.hpp b/source/core/StarLua.hpp index 57afb98..fa777c2 100644 --- a/source/core/StarLua.hpp +++ b/source/core/StarLua.hpp @@ -1050,6 +1050,9 @@ struct LuaContainerConverter { template <typename T, typename Allocator> struct LuaConverter<List<T, Allocator>> : LuaContainerConverter<List<T, Allocator>> {}; +template <typename T, typename Allocator, typename Equals> +struct LuaConverter<HashSet<T, Allocator, Equals>> : LuaContainerConverter<HashSet<T, Allocator, Equals>> {}; + template <typename T, size_t MaxSize> struct LuaConverter<StaticList<T, MaxSize>> : LuaContainerConverter<StaticList<T, MaxSize>> {}; diff --git a/source/core/StarNetCompatibility.cpp b/source/core/StarNetCompatibility.cpp index cce74dc..220e31e 100644 --- a/source/core/StarNetCompatibility.cpp +++ b/source/core/StarNetCompatibility.cpp @@ -2,6 +2,6 @@ namespace Star { -VersionNumber const OpenProtocolVersion = 4; +VersionNumber const OpenProtocolVersion = 6; // update StreamCompatibilityVersion too! }
\ No newline at end of file diff --git a/source/core/StarVersion.cpp.in b/source/core/StarVersion.cpp.in index 0529c19..bb6ae0b 100644 --- a/source/core/StarVersion.cpp.in +++ b/source/core/StarVersion.cpp.in @@ -2,6 +2,7 @@ namespace Star { +char const* const OpenStarVersionString = "0.1.9"; char const* const StarVersionString = "1.4.4"; char const* const StarSourceIdentifierString = "${STAR_SOURCE_IDENTIFIER}"; char const* const StarArchitectureString = "${STAR_SYSTEM} ${STAR_ARCHITECTURE}"; diff --git a/source/core/StarVersion.hpp b/source/core/StarVersion.hpp index ad65875..90bc7ea 100644 --- a/source/core/StarVersion.hpp +++ b/source/core/StarVersion.hpp @@ -4,6 +4,7 @@ namespace Star { +extern char const* const OpenStarVersionString; extern char const* const StarVersionString; extern char const* const StarSourceIdentifierString; extern char const* const StarArchitectureString; diff --git a/source/frontend/StarCharSelection.cpp b/source/frontend/StarCharSelection.cpp index 2c00b42..c2cc1dc 100644 --- a/source/frontend/StarCharSelection.cpp +++ b/source/frontend/StarCharSelection.cpp @@ -15,8 +15,8 @@ CharSelectionPane::CharSelectionPane(PlayerStoragePtr playerStorage, DeleteCharacterCallback deleteCallback) : m_playerStorage(playerStorage), m_downScroll(0), - m_filteredList({}), m_search(""), + m_filteredList({}), m_createCallback(createCallback), m_selectCallback(selectCallback), m_deleteCallback(deleteCallback) { diff --git a/source/frontend/StarInterfaceLuaBindings.cpp b/source/frontend/StarInterfaceLuaBindings.cpp index cbacb59..085cc4c 100644 --- a/source/frontend/StarInterfaceLuaBindings.cpp +++ b/source/frontend/StarInterfaceLuaBindings.cpp @@ -55,9 +55,9 @@ LuaCallbacks LuaBindings::makeChatCallbacks(MainInterface* mainInterface, Univer auto chat = as<Chat>(mainInterface->paneManager()->registeredPane(MainInterfacePanes::Chat).get()); - callbacks.registerCallback("send", [chat, client](String const& message, Maybe<String> modeName, Maybe<bool> speak) { + callbacks.registerCallback("send", [chat, client](String const& message, Maybe<String> modeName, Maybe<bool> speak, Maybe<JsonObject> data) { auto sendMode = modeName ? ChatSendModeNames.getLeft(*modeName) : ChatSendMode::Broadcast; - client->sendChat(message, sendMode, speak); + client->sendChat(message, sendMode, speak, data); }); // just for SE compat - this shoulda been a utility callback :moyai: diff --git a/source/frontend/StarTitleScreen.cpp b/source/frontend/StarTitleScreen.cpp index a32b51a..f1ffcd9 100644 --- a/source/frontend/StarTitleScreen.cpp +++ b/source/frontend/StarTitleScreen.cpp @@ -213,6 +213,15 @@ void TitleScreen::setMultiPlayerPassword(String password) { m_password = std::move(password); } +bool TitleScreen::multiPlayerForceLegacy() const { + return m_forceLegacy; +} + +void TitleScreen::setMultiPlayerForceLegacy(bool const& forceLegacy) { + m_multiPlayerMenu->fetchChild<ButtonWidget>("legacyCheckbox")->setChecked(forceLegacy); + m_forceLegacy = forceLegacy; +} + void TitleScreen::initMainMenu() { m_mainMenu = make_shared<Pane>(); auto backMenu = make_shared<Pane>(); @@ -347,7 +356,8 @@ void TitleScreen::initMultiPlayerMenu() { {"address", multiPlayerAddress()}, {"account", multiPlayerAccount()}, {"port", multiPlayerPort()}, - //{"password", multiPlayerPassword()} + //{"password", multiPlayerPassword()}, + {"forceLegacy",multiPlayerForceLegacy()} }; auto serverList = m_serverSelectPane->fetchChild<ListWidget>("serverSelectArea.serverList"); @@ -384,6 +394,7 @@ void TitleScreen::initMultiPlayerMenu() { setMultiPlayerPort(data.getString("port", "")); setMultiPlayerAccount(data.getString("account", "")); setMultiPlayerPassword(data.getString("password", "")); + setMultiPlayerForceLegacy(data.getBool("forceLegacy", false)); if (auto passwordWidget = m_multiPlayerMenu->fetchChild("password")) passwordWidget->focus(); @@ -406,6 +417,10 @@ void TitleScreen::initMultiPlayerMenu() { readerConnect.registerCallback("password", [=](Widget* obj) { m_password = convert<TextBoxWidget>(obj)->getText().trim(); }); + + readerConnect.registerCallback("legacyCheckbox", [=](Widget* obj) { + m_forceLegacy = convert<ButtonWidget>(obj)->isChecked(); + }); readerConnect.registerCallback("connect", [=](Widget*) { switchState(TitleState::StartMultiPlayer); diff --git a/source/frontend/StarTitleScreen.hpp b/source/frontend/StarTitleScreen.hpp index 3199247..095774f 100644 --- a/source/frontend/StarTitleScreen.hpp +++ b/source/frontend/StarTitleScreen.hpp @@ -77,6 +77,9 @@ public: String multiPlayerPassword() const; void setMultiPlayerPassword(String password); + bool multiPlayerForceLegacy() const; + void setMultiPlayerForceLegacy(bool const& forceLegacy); + private: void initMainMenu(); void initCharSelectionMenu(); @@ -122,6 +125,7 @@ private: String m_connectionPort; String m_account; String m_password; + bool m_forceLegacy; CelestialMasterDatabasePtr m_celestialDatabase; diff --git a/source/game/CMakeLists.txt b/source/game/CMakeLists.txt index 11e0024..b1a519c 100644 --- a/source/game/CMakeLists.txt +++ b/source/game/CMakeLists.txt @@ -250,6 +250,7 @@ SET (star_game_HEADERS scripting/StarPlayerLuaBindings.hpp scripting/StarRootLuaBindings.hpp scripting/StarScriptedAnimatorLuaBindings.hpp + scripting/StarSongbookLuaBindings.hpp scripting/StarStatusControllerLuaBindings.hpp scripting/StarTeamClientLuaBindings.hpp scripting/StarWorldLuaBindings.hpp @@ -491,6 +492,7 @@ SET (star_game_SOURCES scripting/StarPlayerLuaBindings.cpp scripting/StarRootLuaBindings.cpp scripting/StarScriptedAnimatorLuaBindings.cpp + scripting/StarSongbookLuaBindings.cpp scripting/StarStatusControllerLuaBindings.cpp scripting/StarTeamClientLuaBindings.cpp scripting/StarWorldLuaBindings.cpp diff --git a/source/game/StarChatProcessor.cpp b/source/game/StarChatProcessor.cpp index 2082529..0dd0c48 100644 --- a/source/game/StarChatProcessor.cpp +++ b/source/game/StarChatProcessor.cpp @@ -122,7 +122,7 @@ StringList ChatProcessor::activeChannels() const { return channels; } -void ChatProcessor::broadcast(ConnectionId sourceConnectionId, String const& text) { +void ChatProcessor::broadcast(ConnectionId sourceConnectionId, String const& text, JsonObject data) { RecursiveMutexLocker locker(m_mutex); ChatReceivedMessage message = { @@ -132,6 +132,8 @@ void ChatProcessor::broadcast(ConnectionId sourceConnectionId, String const& tex text }; + message.data = std::move(data); + if (handleCommand(message)) return; @@ -139,7 +141,7 @@ void ChatProcessor::broadcast(ConnectionId sourceConnectionId, String const& tex pair.second.pendingMessages.append(message); } -void ChatProcessor::message(ConnectionId sourceConnectionId, MessageContext::Mode mode, String const& channelName, String const& text) { +void ChatProcessor::message(ConnectionId sourceConnectionId, MessageContext::Mode mode, String const& channelName, String const& text, JsonObject data) { RecursiveMutexLocker locker(m_mutex); ChatReceivedMessage message = { @@ -149,6 +151,8 @@ void ChatProcessor::message(ConnectionId sourceConnectionId, MessageContext::Mod text }; + message.data = std::move(data); + if (handleCommand(message)) return; @@ -158,11 +162,17 @@ void ChatProcessor::message(ConnectionId sourceConnectionId, MessageContext::Mod } } -void ChatProcessor::whisper(ConnectionId sourceConnectionId, ConnectionId targetClientId, String const& text) { +void ChatProcessor::whisper(ConnectionId sourceConnectionId, ConnectionId targetClientId, String const& text, JsonObject data) { RecursiveMutexLocker locker(m_mutex); ChatReceivedMessage message = { - {MessageContext::Whisper}, sourceConnectionId, connectionNick(sourceConnectionId), text}; + {MessageContext::Whisper}, + sourceConnectionId, + connectionNick(sourceConnectionId), + text + }; + + message.data = std::move(data); if (handleCommand(message)) return; diff --git a/source/game/StarChatProcessor.hpp b/source/game/StarChatProcessor.hpp index 89bb5f5..1c42b75 100644 --- a/source/game/StarChatProcessor.hpp +++ b/source/game/StarChatProcessor.hpp @@ -41,9 +41,9 @@ public: StringList clientChannels(ConnectionId clientId) const; StringList activeChannels() const; - void broadcast(ConnectionId sourceConnectionId, String const& text); - void message(ConnectionId sourceConnectionId, MessageContext::Mode context, String const& channelName, String const& text); - void whisper(ConnectionId sourceConnectionId, ConnectionId targetClientId, String const& text); + void broadcast(ConnectionId sourceConnectionId, String const& text, JsonObject data = {}); + void message(ConnectionId sourceConnectionId, MessageContext::Mode context, String const& channelName, String const& text, JsonObject data = {}); + void whisper(ConnectionId sourceConnectionId, ConnectionId targetClientId, String const& text, JsonObject data = {}); // Shorthand for passing ServerConnectionId as sourceConnectionId to // broadcast / message / whisper diff --git a/source/game/StarChatTypes.cpp b/source/game/StarChatTypes.cpp index 79dfc4f..56fa6d9 100644 --- a/source/game/StarChatTypes.cpp +++ b/source/game/StarChatTypes.cpp @@ -56,6 +56,7 @@ ChatReceivedMessage::ChatReceivedMessage(Json const& json) : ChatReceivedMessage fromNick = json.getString("fromNick", ""); portrait = json.getString("portrait", ""); text = json.getString("text", ""); + data = json.getObject("data", JsonObject()); } Json ChatReceivedMessage::toJson() const { @@ -67,7 +68,8 @@ Json ChatReceivedMessage::toJson() const { {"fromConnection", fromConnection}, {"fromNick", fromNick.empty() ? Json() : fromNick}, {"portrait", portrait.empty() ? Json() : portrait}, - {"text", text} + {"text", text}, + {"data", data} }; } @@ -78,7 +80,8 @@ DataStream& operator>>(DataStream& ds, ChatReceivedMessage& receivedMessage) { ds.read(receivedMessage.fromNick); ds.read(receivedMessage.portrait); ds.read(receivedMessage.text); - + if (ds.streamCompatibilityVersion() >= 5) + ds.read(receivedMessage.data); return ds; } @@ -88,7 +91,8 @@ DataStream& operator<<(DataStream& ds, ChatReceivedMessage const& receivedMessag ds.write(receivedMessage.fromNick); ds.write(receivedMessage.portrait); ds.write(receivedMessage.text); - + if (ds.streamCompatibilityVersion() >= 5) + ds.write(receivedMessage.data); return ds; } diff --git a/source/game/StarChatTypes.hpp b/source/game/StarChatTypes.hpp index 3feb413..3c23d7c 100644 --- a/source/game/StarChatTypes.hpp +++ b/source/game/StarChatTypes.hpp @@ -55,6 +55,8 @@ struct ChatReceivedMessage { String portrait; String text; + + JsonObject data; }; DataStream& operator>>(DataStream& ds, ChatReceivedMessage& receivedMessage); diff --git a/source/game/StarItemBag.cpp b/source/game/StarItemBag.cpp index 8abdd6f..1f8f74f 100644 --- a/source/game/StarItemBag.cpp +++ b/source/game/StarItemBag.cpp @@ -234,17 +234,22 @@ auto ItemBag::itemsFitWhere(ItemConstPtr const& items, uint64_t max) const -> It return ItemsFitWhereResult(); List<size_t> slots; + StableHashSet<size_t> taken; uint64_t count = std::min(items->count(), max); while (true) { if (count == 0) break; - size_t slot = bestSlotAvailable(items, false); + size_t slot = bestSlotAvailable(items, false, [&](size_t i) { + return !taken.contains(i); + }); if (slot == NPos) break; - else + else { slots.append(slot); + taken.insert(slot); + } uint64_t available = stackTransfer(at(slot), items); if (available != 0) @@ -350,9 +355,11 @@ uint64_t ItemBag::stackTransfer(ItemConstPtr const& to, ItemConstPtr const& from return std::min(to->maxStack() - to->count(), from->count()); } -size_t ItemBag::bestSlotAvailable(ItemConstPtr const& item, bool stacksOnly) const { +size_t ItemBag::bestSlotAvailable(ItemConstPtr const& item, bool stacksOnly, std::function<bool(size_t)> test) const { // First look for any slots that can stack, before empty slots. for (size_t i = 0; i < m_items.size(); ++i) { + if (!test(i)) + continue; auto const& storedItem = at(i); if (storedItem && stackTransfer(storedItem, item) != 0) return i; @@ -369,4 +376,8 @@ size_t ItemBag::bestSlotAvailable(ItemConstPtr const& item, bool stacksOnly) con return NPos; } +size_t ItemBag::bestSlotAvailable(ItemConstPtr const& item, bool stacksOnly) const { + return bestSlotAvailable(item, stacksOnly, [](size_t) { return true; }); +} + } diff --git a/source/game/StarItemBag.hpp b/source/game/StarItemBag.hpp index 7b0246d..7480d7c 100644 --- a/source/game/StarItemBag.hpp +++ b/source/game/StarItemBag.hpp @@ -109,6 +109,7 @@ private: // Returns the slot that contains the item already and has the *highest* // stack count but not full, or an empty slot, or NPos for no room. + size_t bestSlotAvailable(ItemConstPtr const& item, bool stacksOnly, std::function<bool(size_t)> test) const; size_t bestSlotAvailable(ItemConstPtr const& item, bool stacksOnly) const; List<ItemPtr> m_items; diff --git a/source/game/StarMonster.cpp b/source/game/StarMonster.cpp index d9ce986..98f4642 100644 --- a/source/game/StarMonster.cpp +++ b/source/game/StarMonster.cpp @@ -144,7 +144,7 @@ void Monster::init(World* world, EntityId entityId, EntityMode mode) { m_scriptComponent.addCallbacks("entity", LuaBindings::makeEntityCallbacks(this)); m_scriptComponent.addCallbacks("animator", LuaBindings::makeNetworkedAnimatorCallbacks(&m_networkedAnimator)); m_scriptComponent.addCallbacks("status", LuaBindings::makeStatusControllerCallbacks(m_statusController.get())); - m_scriptComponent.addCallbacks("behavior", LuaBindings::makeBehaviorLuaCallbacks(&m_behaviors)); + m_scriptComponent.addCallbacks("behavior", LuaBindings::makeBehaviorCallbacks(&m_behaviors)); m_scriptComponent.addActorMovementCallbacks(m_movementController.get()); m_scriptComponent.init(world); } diff --git a/source/game/StarNetPackets.cpp b/source/game/StarNetPackets.cpp index 4b16ce8..a121beb 100644 --- a/source/game/StarNetPackets.cpp +++ b/source/game/StarNetPackets.cpp @@ -495,14 +495,20 @@ ChatSendPacket::ChatSendPacket() : sendMode(ChatSendMode::Broadcast) {} ChatSendPacket::ChatSendPacket(String text, ChatSendMode sendMode) : text(std::move(text)), sendMode(sendMode) {} +ChatSendPacket::ChatSendPacket(String text, ChatSendMode sendMode, JsonObject data) : text(std::move(text)), sendMode(sendMode), data(std::move(data)) {} + void ChatSendPacket::read(DataStream& ds) { ds.read(text); ds.read(sendMode); + if (ds.streamCompatibilityVersion() >= 5) + ds.read(data); } void ChatSendPacket::write(DataStream& ds) const { ds.write(text); ds.write(sendMode); + if (ds.streamCompatibilityVersion() >= 5) + ds.write(data); } CelestialRequestPacket::CelestialRequestPacket() {} diff --git a/source/game/StarNetPackets.hpp b/source/game/StarNetPackets.hpp index 8db5c03..63a7ee8 100644 --- a/source/game/StarNetPackets.hpp +++ b/source/game/StarNetPackets.hpp @@ -371,12 +371,14 @@ struct FlyShipPacket : PacketBase<PacketType::FlyShip> { struct ChatSendPacket : PacketBase<PacketType::ChatSend> { ChatSendPacket(); ChatSendPacket(String text, ChatSendMode sendMode); + ChatSendPacket(String text, ChatSendMode sendMode, JsonObject data); void read(DataStream& ds) override; void write(DataStream& ds) const override; String text; ChatSendMode sendMode; + JsonObject data; }; struct CelestialRequestPacket : PacketBase<PacketType::CelestialRequest> { diff --git a/source/game/StarNpc.cpp b/source/game/StarNpc.cpp index 388f6e5..7c1cf63 100644 --- a/source/game/StarNpc.cpp +++ b/source/game/StarNpc.cpp @@ -2,6 +2,8 @@ #include "StarDataStreamExtra.hpp" #include "StarWorld.hpp" #include "StarRoot.hpp" +#include "StarSongbook.hpp" +#include "StarSongbookLuaBindings.hpp" #include "StarDamageManager.hpp" #include "StarDamageDatabase.hpp" #include "StarLogging.hpp" @@ -76,6 +78,8 @@ Npc::Npc(NpcVariant const& npcVariant) if (!m_statusController->statusProperty("effectDirectives")) m_statusController->setStatusProperty("effectDirectives", speciesDefinition->effectDirectives()); + m_songbook = make_shared<Songbook>(species()); + m_effectEmitter = make_shared<EffectEmitter>(); m_hitDamageNotificationLimiter = 0; @@ -185,7 +189,8 @@ void Npc::init(World* world, EntityId entityId, EntityMode mode) { { return m_npcVariant.scriptConfig.query(name, def); })); m_scriptComponent.addCallbacks("entity", LuaBindings::makeEntityCallbacks(this)); m_scriptComponent.addCallbacks("status", LuaBindings::makeStatusControllerCallbacks(m_statusController.get())); - m_scriptComponent.addCallbacks("behavior", LuaBindings::makeBehaviorLuaCallbacks(&m_behaviors)); + m_scriptComponent.addCallbacks("behavior", LuaBindings::makeBehaviorCallbacks(&m_behaviors)); + m_scriptComponent.addCallbacks("songbook", LuaBindings::makeSongbookCallbacks(m_songbook.get())); m_scriptComponent.addActorMovementCallbacks(m_movementController.get()); m_scriptComponent.init(world); } @@ -199,6 +204,8 @@ void Npc::uninit() { m_scriptComponent.removeCallbacks("config"); m_scriptComponent.removeCallbacks("entity"); m_scriptComponent.removeCallbacks("status"); + m_scriptComponent.removeCallbacks("behavior"); + m_scriptComponent.removeCallbacks("songbook"); m_scriptComponent.removeActorMovementCallbacks(); } m_tools->uninit(); @@ -359,6 +366,8 @@ void Npc::destroy(RenderCallback* renderCallback) { if (renderCallback && m_deathParticleBurst.get()) renderCallback->addParticles(m_humanoid.particles(*m_deathParticleBurst.get()), position()); + + m_songbook->stop(); } void Npc::damagedOther(DamageNotification const& damage) { @@ -503,6 +512,7 @@ void Npc::render(RenderCallback* renderCallback) { renderCallback->addDrawables(m_tools->renderObjectPreviews(aimPosition(), walkingDirection(), inToolRange(), favoriteColor()), renderLayer); m_effectEmitter->render(renderCallback); + m_songbook->render(renderCallback); } void Npc::renderLightSources(RenderCallback* renderCallback) { @@ -571,6 +581,8 @@ void Npc::tickShared(float dt) { if (m_hitDamageNotificationLimiter) m_hitDamageNotificationLimiter--; + m_songbook->update(*entityMode(), world()); + m_effectEmitter->setSourcePosition("normal", position()); m_effectEmitter->setSourcePosition("mouth", position() + mouthOffset()); m_effectEmitter->setSourcePosition("feet", position() + feetOffset()); @@ -814,6 +826,8 @@ void Npc::setupNetStates() { m_netGroup.addNetElement(m_statusController.get()); m_netGroup.addNetElement(m_armor.get()); m_netGroup.addNetElement(m_tools.get()); + m_songbook->setCompatibilityVersion(6); + m_netGroup.addNetElement(m_songbook.get()); m_netGroup.setNeedsStoreCallback(bind(&Npc::setNetStates, this)); m_netGroup.setNeedsLoadCallback(bind(&Npc::getNetStates, this, _1)); @@ -1075,10 +1089,13 @@ bool Npc::consumeEnergy(float energy) { void Npc::queueUIMessage(String const&) {} bool Npc::instrumentPlaying() { - return false; // TODO: remove this from tool user entirely + return m_songbook->instrumentPlaying(); } -void Npc::instrumentEquipped(String const&) {} +void Npc::instrumentEquipped(String const& instrumentKind) { + if (canUseTool()) + m_songbook->keepAlive(instrumentKind, mouthPosition()); +} void Npc::interact(InteractAction const&) {} @@ -1102,6 +1119,10 @@ StatusController* Npc::statusController() { return m_statusController.get(); } +Songbook* Npc::songbook() { + return m_songbook.get(); +} + void Npc::setCameraFocusEntity(Maybe<EntityId> const&) { // players only } diff --git a/source/game/StarNpc.hpp b/source/game/StarNpc.hpp index 0b22702..21c562c 100644 --- a/source/game/StarNpc.hpp +++ b/source/game/StarNpc.hpp @@ -25,6 +25,7 @@ namespace Star { +STAR_CLASS(Songbook); STAR_CLASS(Item); STAR_CLASS(RenderCallback); STAR_CLASS(Npc); @@ -165,6 +166,7 @@ public: void requestEmote(String const& emote) override; ActorMovementController* movementController() override; StatusController* statusController() override; + Songbook* songbook(); void setCameraFocusEntity(Maybe<EntityId> const& cameraFocusEntity) override; void playEmote(HumanoidEmote emote) override; @@ -246,6 +248,7 @@ private: ArmorWearerPtr m_armor; ToolUserPtr m_tools; + SongbookPtr m_songbook; NetElementBool m_disableWornArmor; diff --git a/source/game/StarPlayer.cpp b/source/game/StarPlayer.cpp index 43add67..facde2e 100644 --- a/source/game/StarPlayer.cpp +++ b/source/game/StarPlayer.cpp @@ -3,6 +3,7 @@ #include "StarJsonExtra.hpp" #include "StarRoot.hpp" #include "StarSongbook.hpp" +#include "StarSongbookLuaBindings.hpp" #include "StarEmoteProcessor.hpp" #include "StarSpeciesDatabase.hpp" #include "StarDamageManager.hpp" @@ -331,6 +332,7 @@ void Player::init(World* world, EntityId entityId, EntityMode mode) { p.second->addActorMovementCallbacks(m_movementController.get()); p.second->addCallbacks("player", LuaBindings::makePlayerCallbacks(this)); p.second->addCallbacks("status", LuaBindings::makeStatusControllerCallbacks(m_statusController.get())); + p.second->addCallbacks("songbook", LuaBindings::makeSongbookCallbacks(m_songbook.get())); if (m_client) p.second->addCallbacks("celestial", LuaBindings::makeCelestialCallbacks(m_client)); p.second->init(world); @@ -362,6 +364,7 @@ void Player::uninit() { p.second->removeCallbacks("player"); p.second->removeCallbacks("mcontroller"); p.second->removeCallbacks("status"); + p.second->removeCallbacks("songbook"); p.second->removeCallbacks("world"); if (m_client) p.second->removeCallbacks("celestial"); @@ -2272,7 +2275,7 @@ bool Player::instrumentPlaying() { void Player::instrumentEquipped(String const& instrumentKind) { if (canUseTool()) - m_songbook->keepalive(instrumentKind, mouthPosition()); + m_songbook->keepAlive(instrumentKind, mouthPosition()); } void Player::interact(InteractAction const& action) { diff --git a/source/game/StarPlayerStorage.cpp b/source/game/StarPlayerStorage.cpp index 7687e97..6be4460 100644 --- a/source/game/StarPlayerStorage.cpp +++ b/source/game/StarPlayerStorage.cpp @@ -173,7 +173,7 @@ Json PlayerStorage::savePlayer(PlayerPtr const& player) { VersionedJson versionedJson = entityFactory->storeVersionedJson(EntityType::Player, playerCacheData); auto fileName = strf("{}.player", uuidFileName(uuid)); VersionedJson::writeFile(versionedJson, File::relativeTo(m_storageDirectory, fileName)); - Logger::info("Saved player {} to {}", Text::stripEscapeCodes(player->name()), fileName); + Logger::debug("Saved player {} to {}", Text::stripEscapeCodes(player->name()), fileName); } return newPlayerData; } diff --git a/source/game/StarSkyParameters.cpp b/source/game/StarSkyParameters.cpp index e2ed885..3d540b8 100644 --- a/source/game/StarSkyParameters.cpp +++ b/source/game/StarSkyParameters.cpp @@ -175,6 +175,8 @@ void SkyParameters::read(DataStream& ds) { ds >> sunType; if (ds.streamCompatibilityVersion() >= 3) ds >> settings; + else + settings = JsonObject(); } void SkyParameters::write(DataStream& ds) const { diff --git a/source/game/StarSongbook.cpp b/source/game/StarSongbook.cpp index 68db655..72f2b2b 100644 --- a/source/game/StarSongbook.cpp +++ b/source/game/StarSongbook.cpp @@ -158,7 +158,7 @@ void Songbook::render(RenderCallback* renderCallback) { m_pendingAudio.clear(); } -void Songbook::keepalive(String const& instrument, Vec2F const& position) { +void Songbook::keepAlive(String const& instrument, Vec2F const& position) { if (instrument != m_instrument) { m_instrument = instrument; m_dataUpdated = true; @@ -681,11 +681,11 @@ void Songbook::play(Json const& song, String const& timeSource) { m_activeCooldown = 3; } -bool Songbook::active() { +bool Songbook::active() const { return m_activeCooldown > 0; } -bool Songbook::instrumentPlaying() { +bool Songbook::instrumentPlaying() const { if (!active()) return false; if (m_timeSourceInstance) { @@ -698,6 +698,18 @@ bool Songbook::instrumentPlaying() { return false; } +Maybe<String> Songbook::timeSource() const { + return m_timeSource; +} + +Maybe<String> Songbook::instrument() const { + return m_instrument; +} + +Json Songbook::song() const { + return m_song; +} + double Songbook::fundamentalFrequency(double p) { return 55.0 * pow(2.0, (p - 69.0) / 12.0 + 3.0); } diff --git a/source/game/StarSongbook.hpp b/source/game/StarSongbook.hpp index 89a4bfd..6f15242 100644 --- a/source/game/StarSongbook.hpp +++ b/source/game/StarSongbook.hpp @@ -24,12 +24,16 @@ public: // instrument needs to tell the songbook what type it is, and needs to keep // calling it to signal // the instrument is still equiped - void keepalive(String const& instrument, Vec2F const& position); + void keepAlive(String const& instrument, Vec2F const& position); void stop(); - void play(Json const& song, String const& timesource); - bool active(); - bool instrumentPlaying(); + void play(Json const& song, String const& timeSource); + bool active() const; + bool instrumentPlaying() const; + + Maybe<String> timeSource() const; + Maybe<String> instrument() const; + Json song() const; private: struct Note { diff --git a/source/game/StarStagehand.cpp b/source/game/StarStagehand.cpp index d85f77b..1207d91 100644 --- a/source/game/StarStagehand.cpp +++ b/source/game/StarStagehand.cpp @@ -43,7 +43,7 @@ void Stagehand::init(World* world, EntityId entityId, EntityMode mode) { return m_config.query(name, def); })); m_scriptComponent.addCallbacks("entity", LuaBindings::makeEntityCallbacks(this)); - m_scriptComponent.addCallbacks("behavior", LuaBindings::makeBehaviorLuaCallbacks(&m_behaviors)); + m_scriptComponent.addCallbacks("behavior", LuaBindings::makeBehaviorCallbacks(&m_behaviors)); m_scriptComponent.init(world); } } diff --git a/source/game/StarUniverseClient.cpp b/source/game/StarUniverseClient.cpp index af33508..b41d19e 100644 --- a/source/game/StarUniverseClient.cpp +++ b/source/game/StarUniverseClient.cpp @@ -71,7 +71,7 @@ PlayerPtr UniverseClient::mainPlayer() const { return m_mainPlayer; } -Maybe<String> UniverseClient::connect(UniverseConnection connection, bool allowAssetsMismatch, String const& account, String const& password) { +Maybe<String> UniverseClient::connect(UniverseConnection connection, bool allowAssetsMismatch, String const& account, String const& password, bool const& forceLegacy) { auto& root = Root::singleton(); auto assets = root.assets(); @@ -85,9 +85,11 @@ Maybe<String> UniverseClient::connect(UniverseConnection connection, bool allowA { auto protocolRequest = make_shared<ProtocolRequestPacket>(StarProtocolVersion); - protocolRequest->setCompressionMode(PacketCompressionMode::Enabled); - // Signal that we're OpenStarbound. Vanilla Starbound only compresses - // packets above 64 bytes - by forcing it, we can communicate this. + if (!forceLegacy) { + protocolRequest->setCompressionMode(PacketCompressionMode::Enabled); + // Signal that we're OpenStarbound. Vanilla Starbound only compresses + // packets above 64 bytes - by forcing it, we can communicate this. + } connection.pushSingle(protocolRequest); } connection.sendAll(timeout); @@ -101,7 +103,7 @@ Maybe<String> UniverseClient::connect(UniverseConnection connection, bool allowA NetCompatibilityRules compatibilityRules; compatibilityRules.setVersion(LegacyVersion); - bool legacyServer = protocolResponsePacket->compressionMode() != PacketCompressionMode::Enabled; + bool legacyServer = forceLegacy || (protocolResponsePacket->compressionMode() != PacketCompressionMode::Enabled); if (!legacyServer) { auto compressedSocket = as<CompressedPacketSocket>(&connection.packetSocket()); if (protocolResponsePacket->info) { @@ -484,10 +486,13 @@ bool UniverseClient::flying() const { return false; } -void UniverseClient::sendChat(String const& text, ChatSendMode sendMode, Maybe<bool> speak) { +void UniverseClient::sendChat(String const& text, ChatSendMode sendMode, Maybe<bool> speak, Maybe<JsonObject> data) { if (speak.value(!text.beginsWith("/"))) m_mainPlayer->addChatMessage(text); - m_connection->pushSingle(make_shared<ChatSendPacket>(text, sendMode)); + auto packet = make_shared<ChatSendPacket>(text, sendMode); + if (data) + packet->data = std::move(*data); + m_connection->pushSingle(packet); } List<ChatReceivedMessage> UniverseClient::pullChatMessages() { diff --git a/source/game/StarUniverseClient.hpp b/source/game/StarUniverseClient.hpp index b770b07..b1c589f 100644 --- a/source/game/StarUniverseClient.hpp +++ b/source/game/StarUniverseClient.hpp @@ -41,7 +41,7 @@ public: PlayerPtr mainPlayer() const; // Returns error if connection failed - Maybe<String> connect(UniverseConnection connection, bool allowAssetsMismatch, String const& account = "", String const& password = ""); + Maybe<String> connect(UniverseConnection connection, bool allowAssetsMismatch, String const& account = "", String const& password = "", bool const& forceLegacy = false); bool isConnected() const; void disconnect(); Maybe<String> disconnectReason() const; @@ -80,7 +80,7 @@ public: SkyConstPtr currentSky() const; bool flying() const; - void sendChat(String const& text, ChatSendMode sendMode, Maybe<bool> speak = {}); + void sendChat(String const& text, ChatSendMode sendMode, Maybe<bool> speak = {}, Maybe<JsonObject> data = {}); List<ChatReceivedMessage> pullChatMessages(); uint16_t players(); diff --git a/source/game/StarUniverseServer.cpp b/source/game/StarUniverseServer.cpp index f32f650..977dc71 100644 --- a/source/game/StarUniverseServer.cpp +++ b/source/game/StarUniverseServer.cpp @@ -1064,16 +1064,19 @@ void UniverseServer::processChat() { for (auto const& p : take(m_pendingChat)) { if (auto clientContext = m_clients.get(p.first)) { for (auto const& chat : p.second) { + auto& message = get<0>(chat); + auto sendMode = get<1>(chat); + auto& data = get<2>(chat); if (clientContext->remoteAddress()) - Logger::info("Chat: <{}> {}", clientContext->playerName(), chat.first); + Logger::info("Chat: <{}> {}", clientContext->playerName(), message); auto team = m_teamManager->getTeam(clientContext->playerUuid()); - if (chat.second == ChatSendMode::Broadcast) - m_chatProcessor->broadcast(p.first, chat.first); - else if (chat.second == ChatSendMode::Party && team.isValid()) - m_chatProcessor->message(p.first, MessageContext::Mode::Party, team.value().hex(), chat.first); + if (sendMode == ChatSendMode::Broadcast) + m_chatProcessor->broadcast(p.first, message, std::move(data)); + else if (sendMode == ChatSendMode::Party && team.isValid()) + m_chatProcessor->message(p.first, MessageContext::Mode::Party, team.value().hex(), message, std::move(data)); else - m_chatProcessor->message(p.first, MessageContext::Mode::Local, printWorldId(clientContext->playerWorldId()), chat.first); + m_chatProcessor->message(p.first, MessageContext::Mode::Local, printWorldId(clientContext->playerWorldId()), message, std::move(data)); } } } @@ -1527,7 +1530,7 @@ void UniverseServer::packetsReceived(UniverseConnectionServer*, ConnectionId cli } else if (auto chatSend = as<ChatSendPacket>(packet)) { RecursiveMutexLocker locker(m_mainLock); - m_pendingChat[clientId].append({std::move(chatSend->text), chatSend->sendMode}); + m_pendingChat[clientId].append(make_tuple(std::move(chatSend->text), chatSend->sendMode, std::move(chatSend->data))); } else if (auto clientContextUpdatePacket = as<ClientContextUpdatePacket>(packet)) { clientContext->readUpdate(std::move(clientContextUpdatePacket->updateData)); diff --git a/source/game/StarUniverseServer.hpp b/source/game/StarUniverseServer.hpp index f3db6fc..ddf7d0c 100644 --- a/source/game/StarUniverseServer.hpp +++ b/source/game/StarUniverseServer.hpp @@ -254,7 +254,7 @@ private: HashMap<ConnectionId, String> m_pendingDisconnections; HashMap<ConnectionId, List<WorkerPoolPromise<CelestialResponse>>> m_pendingCelestialRequests; List<pair<WorldId, UniverseFlagAction>> m_pendingFlagActions; - HashMap<ConnectionId, List<pair<String, ChatSendMode>>> m_pendingChat; + HashMap<ConnectionId, List<tuple<String, ChatSendMode, JsonObject>>> m_pendingChat; Maybe<WorkerPoolPromise<CelestialCoordinate>> m_nextRandomizedStarterWorld; Map<WorldId, List<WorldServerThread::Message>> m_pendingWorldMessages; diff --git a/source/game/scripting/StarBehaviorLuaBindings.cpp b/source/game/scripting/StarBehaviorLuaBindings.cpp index 8866746..4bf16f2 100644 --- a/source/game/scripting/StarBehaviorLuaBindings.cpp +++ b/source/game/scripting/StarBehaviorLuaBindings.cpp @@ -4,7 +4,7 @@ namespace Star { -LuaCallbacks LuaBindings::makeBehaviorLuaCallbacks(List<BehaviorStatePtr>* list) { +LuaCallbacks LuaBindings::makeBehaviorCallbacks(List<BehaviorStatePtr>* list) { LuaCallbacks callbacks; callbacks.registerCallback("behavior", [list](Json const& config, JsonObject const& parameters, LuaTable context, Maybe<LuaUserData> blackboard) -> BehaviorStateWeakPtr { diff --git a/source/game/scripting/StarBehaviorLuaBindings.hpp b/source/game/scripting/StarBehaviorLuaBindings.hpp index d2a882b..01c04ab 100644 --- a/source/game/scripting/StarBehaviorLuaBindings.hpp +++ b/source/game/scripting/StarBehaviorLuaBindings.hpp @@ -9,6 +9,6 @@ STAR_CLASS(Root); STAR_CLASS(UniverseClient); namespace LuaBindings { - LuaCallbacks makeBehaviorLuaCallbacks(List<BehaviorStatePtr>* list); + LuaCallbacks makeBehaviorCallbacks(List<BehaviorStatePtr>* list); } } diff --git a/source/game/scripting/StarPlayerLuaBindings.cpp b/source/game/scripting/StarPlayerLuaBindings.cpp index 5af86d0..ea2bae5 100644..100755 --- a/source/game/scripting/StarPlayerLuaBindings.cpp +++ b/source/game/scripting/StarPlayerLuaBindings.cpp @@ -496,6 +496,10 @@ LuaCallbacks LuaBindings::makePlayerCallbacks(Player* player) { return QuestStateNames.getRight(player->questManager()->getQuest(questId)->state()); }); + callbacks.registerCallback("questObjectives", [player](String const& questId) -> Maybe<JsonArray> { + return player->questManager()->getQuest(questId)->objectiveList(); + }); + callbacks.registerCallback("callQuest", [player](String const& questId, String const& func, LuaVariadic<LuaValue> const& args) -> Maybe<LuaValue> { if (!player->questManager()->hasQuest(questId)) return {}; @@ -522,8 +526,11 @@ LuaCallbacks LuaBindings::makePlayerCallbacks(Player* player) { return player->questManager()->trackedQuestId(); }); - callbacks.registerCallback("setTrackedQuest", [player](Maybe<String> const& questId) { - return player->questManager()->setAsTracked(questId); + callbacks.registerCallback("setTrackedQuest", [player](String const& questId) { + if (!player->questManager()->isCurrent(questId)) + return player->questManager()->setAsTracked(questId); + else + return player->questManager()->setAsTracked({}); }); callbacks.registerCallback("canTurnInQuest", [player](String const& questId) { diff --git a/source/game/scripting/StarRootLuaBindings.cpp b/source/game/scripting/StarRootLuaBindings.cpp index c45ef46..4e60595 100644 --- a/source/game/scripting/StarRootLuaBindings.cpp +++ b/source/game/scripting/StarRootLuaBindings.cpp @@ -66,13 +66,12 @@ LuaCallbacks LuaBindings::makeRootCallbacks() { callbacks.registerCallbackWithSignature<Maybe<String>, String, Maybe<String>>("materialMiningSound", bind(RootCallbacks::materialMiningSound, root, _1, _2)); callbacks.registerCallbackWithSignature<Maybe<String>, String, Maybe<String>>("materialFootstepSound", bind(RootCallbacks::materialFootstepSound, root, _1, _2)); - callbacks.registerCallback("assetsByExtension", [root](LuaEngine& engine, String const& extension) -> LuaTable { - auto& extensions = root->assets()->scanExtension(extension); - auto table = engine.createTable(extensions.size(), 0); - size_t i = 0; - for (auto& file : extensions) - table.set(++i, file); - return table; + callbacks.registerCallback("assetsByExtension", [root](String const& extension) -> CaseInsensitiveStringSet { + return root->assets()->scanExtension(extension); + }); + + callbacks.registerCallback("assetsScan", [root]( Maybe<String> const& a, Maybe<String> const& b) -> StringList { + return b ? root->assets()->scan(a.value(), *b) : root->assets()->scan(a.value()); }); callbacks.registerCallback("assetOrigin", [root](String const& path) -> Maybe<String> { @@ -116,12 +115,12 @@ LuaCallbacks LuaBindings::makeRootCallbacks() { return table; }); - callbacks.registerCallback("assetSourceMetadata", [root](LuaEngine& engine, String const& assetSourcePath) { + callbacks.registerCallback("assetSourceMetadata", [root](String const& assetSourcePath) { auto assets = root->assets(); return assets->assetSourceMetadata(assetSourcePath); }); - callbacks.registerCallback("itemFile", [root](LuaEngine& engine, String const& itemName) -> Maybe<String> { + callbacks.registerCallback("itemFile", [root](String const& itemName) -> Maybe<String> { return root->itemDatabase()->itemFile(itemName); }); diff --git a/source/game/scripting/StarScriptableThread.cpp b/source/game/scripting/StarScriptableThread.cpp index cbd32ad..6bbfece 100644 --- a/source/game/scripting/StarScriptableThread.cpp +++ b/source/game/scripting/StarScriptableThread.cpp @@ -14,9 +14,9 @@ namespace Star { ScriptableThread::ScriptableThread(Json parameters) : Thread("ScriptableThread: " + parameters.getString("name")), // TODO m_stop(false), + m_parameters(std::move(parameters)), m_errorOccurred(false), - m_shouldExpire(true), - m_parameters(std::move(parameters)) { + m_shouldExpire(true) { m_luaRoot = make_shared<LuaRoot>(); m_name = m_parameters.getString("name"); @@ -78,8 +78,6 @@ void ScriptableThread::passMessage(Message&& message) { void ScriptableThread::run() { try { - auto& root = Root::singleton(); - double updateMeasureWindow = m_parameters.getDouble("updateMeasureWindow",0.5); TickRateApproacher tickApproacher(1.0f / m_timestep, updateMeasureWindow); diff --git a/source/game/scripting/StarSongbookLuaBindings.cpp b/source/game/scripting/StarSongbookLuaBindings.cpp new file mode 100644 index 0000000..7831ed6 --- /dev/null +++ b/source/game/scripting/StarSongbookLuaBindings.cpp @@ -0,0 +1,21 @@ +#include "StarSongbookLuaBindings.hpp" +#include "StarLuaConverters.hpp" + +namespace Star { + +LuaCallbacks LuaBindings::makeSongbookCallbacks(Songbook* songbook) { + LuaCallbacks callbacks; + + callbacks.registerCallbackWithSignature<void, Json, String>("play", bind(mem_fn(&Songbook::play), songbook, _1, _2)); + callbacks.registerCallbackWithSignature<void, String, Vec2F>("keepAlive", bind(mem_fn(&Songbook::keepAlive), songbook, _1, _2)); + callbacks.registerCallbackWithSignature<void>("stop", bind(mem_fn(&Songbook::stop), songbook)); + callbacks.registerCallbackWithSignature<bool>("active", bind(mem_fn(&Songbook::active), songbook)); + callbacks.registerCallbackWithSignature<Maybe<String>>("band", bind(mem_fn(&Songbook::timeSource), songbook)); + callbacks.registerCallbackWithSignature<Maybe<String>>("instrument", bind(mem_fn(&Songbook::instrument), songbook)); + callbacks.registerCallbackWithSignature<bool>("instrumentPlaying", bind(mem_fn(&Songbook::instrumentPlaying), songbook)); + callbacks.registerCallbackWithSignature<Json>("song", bind(mem_fn(&Songbook::song), songbook)); + + return callbacks; +} + +} diff --git a/source/game/scripting/StarSongbookLuaBindings.hpp b/source/game/scripting/StarSongbookLuaBindings.hpp new file mode 100644 index 0000000..cc55ec6 --- /dev/null +++ b/source/game/scripting/StarSongbookLuaBindings.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include "StarLua.hpp" +#include "StarSongbook.hpp" + +namespace Star { + +namespace LuaBindings { + LuaCallbacks makeSongbookCallbacks(Songbook* songbook); +} +} diff --git a/source/server/main.cpp b/source/server/main.cpp index de09efa..ac40524 100644 --- a/source/server/main.cpp +++ b/source/server/main.cpp @@ -45,7 +45,7 @@ int main(int argc, char** argv) { auto configuration = root->configuration(); { - Logger::info("Server Version {} ({}) Source ID: {} Protocol: {}", StarVersionString, StarArchitectureString, StarSourceIdentifierString, StarProtocolVersion); + Logger::info("OpenStarbound Server v{} for v{} ({}) Source ID: {} Protocol: {}", OpenStarVersionString, StarVersionString, StarArchitectureString, StarSourceIdentifierString, StarProtocolVersion); float updateRate = 1.0f / GlobalTimestep; if (auto jUpdateRate = configuration->get("updateRate")) { |