diff --git a/src/chatproviderservice/package.json b/src/chatproviderservice/package.json index be3f162985..ca3668725e 100644 --- a/src/chatproviderservice/package.json +++ b/src/chatproviderservice/package.json @@ -39,11 +39,13 @@ "@quenty/permissionprovider": "file:../permissionprovider", "@quenty/playerbinder": "file:../playerbinder", "@quenty/playerutils": "file:../playerutils", + "@quenty/preferredparentutils": "file:../preferredparentutils", "@quenty/promise": "file:../promise", "@quenty/richtext": "file:../richtext", "@quenty/rx": "file:../rx", "@quenty/rxbinderutils": "file:../rxbinderutils", "@quenty/servicebag": "file:../servicebag", + "@quenty/signal": "file:../signal", "@quenty/string": "file:../string", "@quenty/table": "file:../table", "@quenty/valueobject": "file:../valueobject" diff --git a/src/chatproviderservice/src/Client/ChatProviderServiceClient.lua b/src/chatproviderservice/src/Client/ChatProviderServiceClient.lua index 8b17f42a53..4bf82f618e 100644 --- a/src/chatproviderservice/src/Client/ChatProviderServiceClient.lua +++ b/src/chatproviderservice/src/Client/ChatProviderServiceClient.lua @@ -7,6 +7,8 @@ local require = require(script.Parent.loader).load(script) local TextChatService = game:GetService("TextChatService") local Players = game:GetService("Players") +local Maid = require("Maid") +local Signal = require("Signal") local String = require("String") local ChatProviderServiceClient = {} @@ -15,11 +17,14 @@ ChatProviderServiceClient.ServiceName = "ChatProviderServiceClient" function ChatProviderServiceClient:Init(serviceBag) assert(not self._serviceBag, "Already initialized") self._serviceBag = assert(serviceBag, "No serviceBag") + self._maid = Maid.new() + + -- State + self.MessageIncoming = self._maid:Add(Signal.new()) -- External self._serviceBag:GetService(require("CmdrServiceClient")) - -- Binders self._serviceBag:GetService(require("ChatTagClient")) self._serviceBag:GetService(require("ChatProviderTranslator")) @@ -28,6 +33,8 @@ end function ChatProviderServiceClient:Start() TextChatService.OnIncomingMessage = function(textChatMessage) + self.MessageIncoming:Fire(textChatMessage) + local textSource = textChatMessage.TextSource if not textSource then return @@ -60,5 +67,8 @@ function ChatProviderServiceClient:_renderTags(textSource) return hasChatTags:GetAsRichText() end +function ChatProviderServiceClient:Destroy() + self._maid:DoCleaning() +end return ChatProviderServiceClient \ No newline at end of file diff --git a/src/chatproviderservice/src/Server/ChatProviderService.lua b/src/chatproviderservice/src/Server/ChatProviderService.lua index 696fe44670..3943fe9395 100644 --- a/src/chatproviderservice/src/Server/ChatProviderService.lua +++ b/src/chatproviderservice/src/Server/ChatProviderService.lua @@ -6,14 +6,17 @@ local require = require(script.Parent.loader).load(script) local ServerScriptService = game:GetService("ServerScriptService") +local TextChatService = game:GetService("TextChatService") local ChatTagDataUtils = require("ChatTagDataUtils") local LocalizedTextUtils = require("LocalizedTextUtils") local Maid = require("Maid") local PermissionLevel = require("PermissionLevel") +local PreferredParentUtils = require("PreferredParentUtils") local Promise = require("Promise") local Rx = require("Rx") local RxBrioUtils = require("RxBrioUtils") +local Signal = require("Signal") local ChatProviderService = {} ChatProviderService.ServiceName = "ChatProviderService" @@ -22,6 +25,9 @@ function ChatProviderService:Init(serviceBag) self._serviceBag = assert(serviceBag, "No serviceBag") self._maid = Maid.new() + -- State + self.MessageIncoming = self._maid:Add(Signal.new()) + -- External self._serviceBag:GetService(require("CmdrService")) self._serviceBag:GetService(require("PermissionService")) @@ -47,6 +53,11 @@ function ChatProviderService:Init(serviceBag) })) end +function ChatProviderService:AddChatCommand(textChatCommand) + assert(typeof(textChatCommand) == "Instance", "Bad textChatCommand") + + textChatCommand.Parent = PreferredParentUtils.getPreferredParent(TextChatService, "ChatProviderCommands") +end --[=[ Sets the developer chat tag diff --git a/src/cmdrservice/package.json b/src/cmdrservice/package.json index 9c4273e1f0..f07ceae2f5 100644 --- a/src/cmdrservice/package.json +++ b/src/cmdrservice/package.json @@ -25,6 +25,7 @@ "Quenty" ], "dependencies": { + "@quenty/chatproviderservice": "file:../chatproviderservice", "@quenty/loader": "file:../loader", "@quenty/maid": "file:../maid", "@quenty/permissionprovider": "file:../permissionprovider", diff --git a/src/cmdrservice/src/Client/CmdrServiceClient.lua b/src/cmdrservice/src/Client/CmdrServiceClient.lua index 4f29646fb1..76ca30970f 100644 --- a/src/cmdrservice/src/Client/CmdrServiceClient.lua +++ b/src/cmdrservice/src/Client/CmdrServiceClient.lua @@ -16,6 +16,8 @@ local Promise = require("Promise") local promiseChild = require("promiseChild") local PromiseUtils = require("PromiseUtils") local String = require("String") +local ChatProviderServiceClient = require("ChatProviderServiceClient") +local Remoting = require("Remoting") local CmdrServiceClient = {} CmdrServiceClient.ServiceName = "CmdrServiceClient" @@ -30,6 +32,7 @@ function CmdrServiceClient:Init(serviceBag) self._maid = Maid.new() self._permissionServiceClient = self._serviceBag:GetService(PermissionServiceClient) + self._chatProviderServiceClient = self._serviceBag:GetService(ChatProviderServiceClient) self:PromiseCmdr():Then(function(cmdr) cmdr.Registry:RegisterHook("BeforeRun", function(context) @@ -69,7 +72,6 @@ function CmdrServiceClient:Init(serviceBag) return nil end end) - end) end @@ -79,6 +81,8 @@ end function CmdrServiceClient:Start() assert(self._serviceBag, "Not initialized") + self._remoting = self._maid:Add(Remoting.new(ReplicatedStorage, "CmdrService")) + self._maid:GivePromise(PromiseUtils.all({ self:PromiseCmdr(), self._maid:GivePromise(self._permissionServiceClient:PromisePermissionProvider()) @@ -106,6 +110,21 @@ function CmdrServiceClient:_setBindings(cmdr) end end)) + self._maid:GiveTask(self._remoting.OpenCmdr:Connect(function() + cmdr:Show() + end)) + + -- same with chat provider + self._maid:GiveTask(self._chatProviderServiceClient.MessageIncoming:Connect(function(textChatMessage) + if not (textChatMessage.TextSource and textChatMessage.TextSource.UserId == Players.LocalPlayer.UserId) then + return + end + + if String.startsWith(textChatMessage.Text, "/cmdr") then + cmdr:Show() + end + end)) + -- Race condition task.defer(function() -- Default blink for debugging purposes diff --git a/src/cmdrservice/src/Server/CmdrService.lua b/src/cmdrservice/src/Server/CmdrService.lua index 5951bab6dc..3d6a27d493 100644 --- a/src/cmdrservice/src/Server/CmdrService.lua +++ b/src/cmdrservice/src/Server/CmdrService.lua @@ -9,11 +9,14 @@ local require = require(script.Parent.loader).load(script) local HttpService = game:GetService("HttpService") +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local Players = game:GetService("Players") local PermissionService = require("PermissionService") local CmdrTemplateProviderServer = require("CmdrTemplateProviderServer") local Promise = require("Promise") local Maid = require("Maid") +local Remoting = require("Remoting") local CmdrService = {} CmdrService.ServiceName = "CmdrService" @@ -25,10 +28,18 @@ local GLOBAL_REGISTRY = setmetatable({}, {__mode = "kv"}) @param serviceBag ServiceBag ]=] function CmdrService:Init(serviceBag) - assert(not self._promiseCmdr, "Already initialized") - + assert(not self._serviceBag, "Already initialized") self._maid = Maid.new() self._serviceBag = assert(serviceBag, "No serviceBag") + + -- State + self._remoting = self._maid:Add(Remoting.new(ReplicatedStorage, "CmdrService")) + self._remoting:DeclareEvent("OpenCmdr") + + -- External + self._chatProviderService = self._serviceBag:GetService(require("ChatProviderService")) + + -- Internal self._cmdrTemplateProviderServer = self._serviceBag:GetService(CmdrTemplateProviderServer) self._serviceId = HttpService:GenerateGUID(false) @@ -86,6 +97,10 @@ function CmdrService:Init(serviceBag) GLOBAL_REGISTRY[self._serviceId] = self end +function CmdrService:Start() + self:_createActivateChatCommand() +end + --[=[ Returns cmdr @return Promise @@ -143,6 +158,29 @@ function CmdrService:RegisterCommand(commandData, execute) end) end +function CmdrService:_createActivateChatCommand() + local command = Instance.new("TextChatCommand") + command.Name = "OpenCmdrCommand" + command.PrimaryAlias = "/cmdr" + + self._maid:GiveTask(command) + self._maid:GiveTask(command.Triggered:Connect(function(originTextSource, _unfilteredText) + local player = Players:GetPlayerByUserId(originTextSource.UserId) + if not player then + return + end + + self._permissionService:PromiseIsAdmin(player):Then(function(isAdmin) + if isAdmin then + self._remoting.OpenCmdr:FireClient(player) + end + end) + end)) + + self._chatProviderService:AddChatCommand(command) +end + + --[=[ Private function used by the execution template to retrieve the execution function. @param cmdrCommandId string diff --git a/src/datastore/src/Server/DataStore.lua b/src/datastore/src/Server/DataStore.lua index 6326a0c315..029e1b628c 100644 --- a/src/datastore/src/Server/DataStore.lua +++ b/src/datastore/src/Server/DataStore.lua @@ -102,17 +102,10 @@ function DataStore.new(robloxDataStore, key) self._robloxDataStore = robloxDataStore or error("No robloxDataStore") self._debugWriting = DEFAULT_DEBUG_WRITING - self._autoSaveTimeSeconds = ValueObject.new(DEFAULT_AUTO_SAVE_TIME_SECONDS) - self._maid:GiveTask(self._autoSaveTimeSeconds) - - self._jitterProportion = ValueObject.new(DEFAULT_JITTER_PROPORTION, "number") - self._maid:GiveTask(self._jitterProportion) - - self._syncOnSave = ValueObject.new(false, "boolean") - self._maid:GiveTask(self._syncOnSave) - - self._loadedOk = ValueObject.new(false, "boolean") - self._maid:GiveTask(self._loadedOk) + self._autoSaveTimeSeconds = self._maid:Add(ValueObject.new(DEFAULT_AUTO_SAVE_TIME_SECONDS)) + self._jitterProportion = self._maid:Add(ValueObject.new(DEFAULT_JITTER_PROPORTION, "number")) + self._syncOnSave = self._maid:Add(ValueObject.new(false, "boolean")) + self._loadedOk = self._maid:Add(ValueObject.new(false, "boolean")) self._userIdList = nil diff --git a/src/gameproductservice/package.json b/src/gameproductservice/package.json index c3cddd1c97..8079729e60 100644 --- a/src/gameproductservice/package.json +++ b/src/gameproductservice/package.json @@ -31,6 +31,7 @@ "@quenty/baseobject": "file:../baseobject", "@quenty/binder": "file:../binder", "@quenty/brio": "file:../brio", + "@quenty/enumutils": "file:../enumutils", "@quenty/gameconfig": "file:../gameconfig", "@quenty/instanceutils": "file:../instanceutils", "@quenty/loader": "file:../loader", diff --git a/src/gameproductservice/src/Server/GameProductService.lua b/src/gameproductservice/src/Server/GameProductService.lua index 305d2f3af9..5e5139f23b 100644 --- a/src/gameproductservice/src/Server/GameProductService.lua +++ b/src/gameproductservice/src/Server/GameProductService.lua @@ -13,8 +13,6 @@ local require = require(script.Parent.loader).load(script) -local Players = game:GetService("Players") - local Maid = require("Maid") local GameProductServiceHelper = require("GameProductServiceHelper") local GameConfigAssetTypeUtils = require("GameConfigAssetTypeUtils") @@ -37,7 +35,7 @@ function GameProductService:Init(serviceBag) -- External self._gameConfigService = self._serviceBag:GetService(require("GameConfigService")) - self._receiptProcessingService = self._serviceBag:GetService(require("ReceiptProcessingService")) + self._serviceBag:GetService(require("ReceiptProcessingService")) -- Internal self._binders = self._serviceBag:GetService(require("GameProductBindersServer")) @@ -84,10 +82,6 @@ function GameProductService:Start() exposeSignal(self.AssetPurchased, GameConfigAssetTypes.ASSET) exposeSignal(self.BundlePurchased, GameConfigAssetTypes.BUNDLE) end)) - - self._maid:GiveTask(self._receiptProcessingService:RegisterReceiptProcessor(function(receiptInfo) - return self:_handleProcessReceipt(receiptInfo) - end)) end --[=[ @@ -200,23 +194,6 @@ function GameProductService:ObservePlayerOwnership(player, assetType, idOrKey) return self._helper:ObservePlayerOwnership(player, assetType, idOrKey) end -function GameProductService:_handleProcessReceipt(receiptInfo) - local player = Players:GetPlayerByUserId(receiptInfo.PlayerId) - if not player then - -- The player probably left the game - -- If they come back, the callback will be called again - return Enum.ProductPurchaseDecision.NotProcessedYet - end - - local productManager = self._binders.PlayerProductManager:Get(player) - if productManager then - return productManager:HandleProcessReceipt(player, receiptInfo) - end - - -- Free money? - return Enum.ProductPurchaseDecision.PurchaseGranted -end - --[=[ Cleans up the game product service ]=] diff --git a/src/gameproductservice/src/Server/Manager/PlayerProductManager.lua b/src/gameproductservice/src/Server/Manager/PlayerProductManager.lua index 93f0c6d100..f0304613d0 100644 --- a/src/gameproductservice/src/Server/Manager/PlayerProductManager.lua +++ b/src/gameproductservice/src/Server/Manager/PlayerProductManager.lua @@ -8,10 +8,12 @@ local require = require(script.Parent.loader).load(script) local BaseObject = require("BaseObject") +local EnumUtils = require("EnumUtils") local GameConfigAssetTypes = require("GameConfigAssetTypes") local GameConfigAssetTypeUtils = require("GameConfigAssetTypeUtils") local GameConfigService = require("GameConfigService") local PlayerMarketeer = require("PlayerMarketeer") +local ReceiptProcessingService = require("ReceiptProcessingService") local Remoting = require("Remoting") local PlayerProductManager = setmetatable({}, BaseObject) @@ -30,6 +32,7 @@ function PlayerProductManager.new(player, serviceBag) self._serviceBag = assert(serviceBag, "No serviceBag") self._gameConfigService = self._serviceBag:GetService(GameConfigService) + self._receiptProcessingService = self._serviceBag:GetService(ReceiptProcessingService) self._marketeer = PlayerMarketeer.new(self._obj, self._gameConfigService:GetConfigPicker()) self._maid:GiveTask(self._marketeer) @@ -45,6 +48,10 @@ function PlayerProductManager.new(player, serviceBag) self:_handlePromptFinished(...) end)) + self._maid:GiveTask(self._receiptProcessingService:ObserveReceiptProcessedForPlayer(self._obj):Subscribe(function(receiptInfo, result) + self:_handleProcessReceipt(receiptInfo, result) + end)) + -- Initialize attributes self._marketeer:GetOwnershipTrackerOrError(GameConfigAssetTypes.PASS):SetWriteAttributesEnabled(true) @@ -73,23 +80,16 @@ function PlayerProductManager:_handlePromptFinished(player, assetType, assetId, assetTracker:HandlePurchaseEvent(assetId, isPurchased) end ---[=[ - Handles the receipt processing. Not expected to be called immediately - - @param player number - @param receiptInfo table - @return ProductPurchaseDecision -]=] -function PlayerProductManager:HandleProcessReceipt(player, receiptInfo) - assert(self._obj == player, "Bad player") +function PlayerProductManager:_handleProcessReceipt(receiptInfo, productPurchaseDecision) assert(type(receiptInfo) == "table", "Bad receiptInfo") + assert(EnumUtils.isOfType(Enum.ProductPurchaseDecision, productPurchaseDecision), "Bad decision") local assetTracker = self._marketeer:GetAssetTrackerOrError(GameConfigAssetTypes.PRODUCT) -- Notify the player self._remoting.NotifyReceiptProcessed:FireClient(self._obj, receiptInfo) - return assetTracker:HandleProcessReceipt(player, receiptInfo) + assetTracker:HandleProcessReceipt(self._obj, receiptInfo) end return PlayerProductManager \ No newline at end of file diff --git a/src/gameproductservice/src/Shared/Trackers/PlayerAssetMarketTracker.lua b/src/gameproductservice/src/Shared/Trackers/PlayerAssetMarketTracker.lua index 3c6d1e0b5f..9f947b5a95 100644 --- a/src/gameproductservice/src/Shared/Trackers/PlayerAssetMarketTracker.lua +++ b/src/gameproductservice/src/Shared/Trackers/PlayerAssetMarketTracker.lua @@ -265,16 +265,12 @@ end @param player Player @param receiptInfo ReceiptInfo - @return ProductPurchaseDecision ]=] function PlayerAssetMarketTracker:HandleProcessReceipt(player, receiptInfo) assert(typeof(player) == "Instance", "Bad player") assert(self._receiptProcessingExpected, "No receiptProcessingExpected") self:_handlePurchaseEvent(receiptInfo.ProductId, true, true) - - -- Always grant... - return Enum.ProductPurchaseDecision.PurchaseGranted end return PlayerAssetMarketTracker \ No newline at end of file diff --git a/src/gamescalingutils/package.json b/src/gamescalingutils/package.json index b1285272c2..6bec5e06f3 100644 --- a/src/gamescalingutils/package.json +++ b/src/gamescalingutils/package.json @@ -28,9 +28,11 @@ "access": "public" }, "dependencies": { + "@quenty/baseobject": "file:../baseobject", "@quenty/blend": "file:../blend", "@quenty/instanceutils": "file:../instanceutils", "@quenty/loader": "file:../loader", - "@quenty/rx": "file:../rx" + "@quenty/rx": "file:../rx", + "@quenty/valueobject": "file:../valueobject" } } diff --git a/src/gamescalingutils/src/Client/GameScalingHelper.lua b/src/gamescalingutils/src/Client/GameScalingHelper.lua new file mode 100644 index 0000000000..e97c6d0027 --- /dev/null +++ b/src/gamescalingutils/src/Client/GameScalingHelper.lua @@ -0,0 +1,77 @@ +--[=[ + @class GameScalingHelper +]=] + +local require = require(script.Parent.loader).load(script) + +local BaseObject = require("BaseObject") +local Rx = require("Rx") +local RxInstanceUtils = require("RxInstanceUtils") +local ValueObject = require("ValueObject") + +local GameScalingHelper = setmetatable({}, BaseObject) +GameScalingHelper.ClassName = "GameScalingHelper" +GameScalingHelper.__index = GameScalingHelper + +function GameScalingHelper.new(screenGui) + local self = setmetatable(BaseObject.new(), GameScalingHelper) + + self._absoluteSize = self._maid:Add(ValueObject.new(Vector2.zero, "Vector2")) + self._isVertical = self._maid:Add(ValueObject.new(false, "boolean")) + self._isSmall = self._maid:Add(ValueObject.new(false, "boolean")) + + self._maid:GiveTask(self._absoluteSize:Observe():Pipe({ + Rx.map(function(size) + if size.x <= 0 or size.y <= 0 then + return false + end + + return size.x/size.y <= 1 + end); + Rx.distinct() + }):Subscribe(function(isVertical) + self._isVertical.Value = isVertical + end)) + + self._maid:GiveTask(self._absoluteSize:Observe():Pipe({ + Rx.map(function(size) + if size.x > 0 and size.y > 0 and math.min(size.x, size.y) < 500 then + return true + else + return false + end + end); + }):Subscribe(function(isSmall) + self._isSmall.Value = isSmall + end)) + + if screenGui then + self:SetScreenGui(screenGui) + end + + return self +end + +function GameScalingHelper:ObserveIsSmall() + return self._isSmall:Observe() +end + +function GameScalingHelper:ObserveIsVertical() + return self._isVertical:Observe() +end + +function GameScalingHelper:GetAbsoluteSizeSetter() + return function(absoluteSize) + self:SetAbsoluteSize(absoluteSize) + end +end + +function GameScalingHelper:SetAbsoluteSize(absoluteSize) + self._absoluteSize:Mount(absoluteSize) +end + +function GameScalingHelper:SetScreenGui(screenGui) + self:SetAbsoluteSize(RxInstanceUtils.observeProperty(screenGui, "AbsoluteSize")) +end + +return GameScalingHelper \ No newline at end of file diff --git a/src/ik/src/Server/Rig/IKRig.lua b/src/ik/src/Server/Rig/IKRig.lua index 0a102a9563..7fec29364b 100644 --- a/src/ik/src/Server/Rig/IKRig.lua +++ b/src/ik/src/Server/Rig/IKRig.lua @@ -11,7 +11,7 @@ local Players = game:GetService("Players") local IKRigBase = require("IKRigBase") local IKConstants = require("IKConstants") local CharacterUtils = require("CharacterUtils") -local Motor6DBindersServer = require("Motor6DBindersServer") +local Motor6DStackHumanoid = require("Motor6DStackHumanoid") local IKRig = setmetatable({}, IKRigBase) IKRig.ClassName = "IKRig" @@ -21,7 +21,6 @@ function IKRig.new(humanoid, serviceBag) local self = setmetatable(IKRigBase.new(humanoid), IKRig) self._serviceBag = assert(serviceBag, "No serviceBag") - self._motor6DBindersServer = self._serviceBag:GetService(Motor6DBindersServer) self._remoteEvent = Instance.new("RemoteEvent") self._remoteEvent.Name = IKConstants.REMOTE_EVENT_NAME @@ -33,7 +32,7 @@ function IKRig.new(humanoid, serviceBag) self:_onServerEvent(...) end)) - self._motor6DBindersServer.Motor6DStackHumanoid:Bind(self._obj) + Motor6DStackHumanoid:Tag(self._obj) self._target = nil diff --git a/src/motor6d/src/Client/Motor6DBindersClient.lua b/src/motor6d/src/Client/Motor6DBindersClient.lua deleted file mode 100644 index b1489839b5..0000000000 --- a/src/motor6d/src/Client/Motor6DBindersClient.lua +++ /dev/null @@ -1,12 +0,0 @@ ---[=[ - @class Motor6DBindersClient -]=] - -local require = require(script.Parent.loader).load(script) - -local BinderProvider = require("BinderProvider") -local Binder = require("Binder") - -return BinderProvider.new(script.Name, function(self, serviceBag) - self:Add(Binder.new("Motor6DStack", require("Motor6DStackClient"), serviceBag)) -end) \ No newline at end of file diff --git a/src/motor6d/src/Client/Motor6DServiceClient.lua b/src/motor6d/src/Client/Motor6DServiceClient.lua index 1298009d4a..599d4c50fc 100644 --- a/src/motor6d/src/Client/Motor6DServiceClient.lua +++ b/src/motor6d/src/Client/Motor6DServiceClient.lua @@ -11,8 +11,8 @@ function Motor6DServiceClient:Init(serviceBag) assert(not self._serviceBag, "Already initialized") self._serviceBag = assert(serviceBag, "No serviceBag") - -- Internal - self._serviceBag:GetService(require("Motor6DBindersClient")) + -- Binders + self._serviceBag:GetService(require("Motor6DStackClient")) end return Motor6DServiceClient \ No newline at end of file diff --git a/src/motor6d/src/Client/Stack/Motor6DStackClient.lua b/src/motor6d/src/Client/Stack/Motor6DStackClient.lua index ce8a5438a1..ef4e98a684 100644 --- a/src/motor6d/src/Client/Stack/Motor6DStackClient.lua +++ b/src/motor6d/src/Client/Stack/Motor6DStackClient.lua @@ -5,6 +5,7 @@ local require = require(script.Parent.loader).load(script) local Motor6DStackBase = require("Motor6DStackBase") +local Binder = require("Binder") local Motor6DStackClient = setmetatable({}, Motor6DStackBase) Motor6DStackClient.ClassName = "Motor6DStackClient" @@ -18,4 +19,4 @@ function Motor6DStackClient.new(obj, serviceBag) return self end -return Motor6DStackClient \ No newline at end of file +return Binder.new("Motor6DStack", Motor6DStackClient) \ No newline at end of file diff --git a/src/motor6d/src/Server/Motor6DBindersServer.lua b/src/motor6d/src/Server/Motor6DBindersServer.lua deleted file mode 100644 index 8e75e9accf..0000000000 --- a/src/motor6d/src/Server/Motor6DBindersServer.lua +++ /dev/null @@ -1,14 +0,0 @@ ---[=[ - @class Motor6DBindersServer -]=] - -local require = require(script.Parent.loader).load(script) - -local BinderProvider = require("BinderProvider") -local Binder = require("Binder") -local PlayerHumanoidBinder = require("PlayerHumanoidBinder") - -return BinderProvider.new(script.Name, function(self, serviceBag) - self:Add(Binder.new("Motor6DStack", require("Motor6DStack"), serviceBag)) - self:Add(PlayerHumanoidBinder.new("Motor6DStackHumanoid", require("Motor6DStackHumanoid"), serviceBag)) -end) \ No newline at end of file diff --git a/src/motor6d/src/Server/Motor6DService.lua b/src/motor6d/src/Server/Motor6DService.lua index 4bd1e5173f..6073e29287 100644 --- a/src/motor6d/src/Server/Motor6DService.lua +++ b/src/motor6d/src/Server/Motor6DService.lua @@ -11,8 +11,9 @@ function Motor6DService:Init(serviceBag) assert(not self._serviceBag, "Already initialized") self._serviceBag = assert(serviceBag, "No serviceBag") - -- Internal - self._serviceBag:GetService(require("Motor6DBindersServer")) + -- Binders + self._serviceBag:GetService(require("Motor6DStack")) + self._serviceBag:GetService(require("Motor6DStackHumanoid")) end return Motor6DService \ No newline at end of file diff --git a/src/motor6d/src/Server/Stack/Motor6DStack.lua b/src/motor6d/src/Server/Stack/Motor6DStack.lua index d14eec2043..8a529652e0 100644 --- a/src/motor6d/src/Server/Stack/Motor6DStack.lua +++ b/src/motor6d/src/Server/Stack/Motor6DStack.lua @@ -5,6 +5,7 @@ local require = require(script.Parent.loader).load(script) local Motor6DStackBase = require("Motor6DStackBase") +local Binder = require("Binder") local Motor6DStack = setmetatable({}, Motor6DStackBase) Motor6DStack.ClassName = "Motor6DStack" @@ -18,4 +19,4 @@ function Motor6DStack.new(obj, serviceBag) return self end -return Motor6DStack \ No newline at end of file +return Binder.new("Motor6DStack", Motor6DStack) \ No newline at end of file diff --git a/src/motor6d/src/Server/Stack/Motor6DStackHumanoid.lua b/src/motor6d/src/Server/Stack/Motor6DStackHumanoid.lua index 22b069df7f..e0f5b01c76 100644 --- a/src/motor6d/src/Server/Stack/Motor6DStackHumanoid.lua +++ b/src/motor6d/src/Server/Stack/Motor6DStackHumanoid.lua @@ -5,9 +5,10 @@ local require = require(script.Parent.loader).load(script) local BaseObject = require("BaseObject") -local Motor6DBindersServer = require("Motor6DBindersServer") -local RxInstanceUtils = require("RxInstanceUtils") +local Motor6DStack = require("Motor6DStack") +local PlayerHumanoidBinder = require("PlayerHumanoidBinder") local RxBrioUtils = require("RxBrioUtils") +local RxInstanceUtils = require("RxInstanceUtils") local Motor6DStackHumanoid = setmetatable({}, BaseObject) Motor6DStackHumanoid.ClassName = "Motor6DStackHumanoid" @@ -17,7 +18,6 @@ function Motor6DStackHumanoid.new(humanoid, serviceBag) local self = setmetatable(BaseObject.new(humanoid), Motor6DStackHumanoid) self._serviceBag = assert(serviceBag, "No serviceBag") - self._motor6DBinders = self._serviceBag:GetService(Motor6DBindersServer) self._maid:GiveTask(self:_observeMotorsBrio():Subscribe(function(brio) if brio:IsDead() then @@ -27,9 +27,9 @@ function Motor6DStackHumanoid.new(humanoid, serviceBag) local motor = brio:GetValue() local maid = brio:ToMaid() - self._motor6DBinders.Motor6DStack:Bind(motor) + Motor6DStack:Tag(motor) maid:GiveTask(function() - self._motor6DBinders.Motor6DStack:Unbind(motor) + Motor6DStack:Untag(motor) end) end)) @@ -48,4 +48,4 @@ function Motor6DStackHumanoid:_observeMotorsBrio() }) end -return Motor6DStackHumanoid \ No newline at end of file +return PlayerHumanoidBinder.new("Motor6DStackHumanoid", Motor6DStackHumanoid) \ No newline at end of file diff --git a/src/particles/README.md b/src/particles/README.md new file mode 100644 index 0000000000..c398239a8c --- /dev/null +++ b/src/particles/README.md @@ -0,0 +1,23 @@ +## Particles + +
+ + Documentation status + + + Discord + + + Build and release status + +
+ +Holds utilitity for playing back particles + +
View docs →
+ +## Installation + +``` +npm install @quenty/particles --save +``` diff --git a/src/particles/default.project.json b/src/particles/default.project.json new file mode 100644 index 0000000000..83db13e95f --- /dev/null +++ b/src/particles/default.project.json @@ -0,0 +1,6 @@ +{ + "name": "particles", + "tree": { + "$path": "src" + } +} diff --git a/src/particles/package.json b/src/particles/package.json new file mode 100644 index 0000000000..647694e5eb --- /dev/null +++ b/src/particles/package.json @@ -0,0 +1,34 @@ +{ + "name": "@quenty/particles", + "version": "1.0.0", + "description": "Holds utilitity for playing back particles", + "keywords": [ + "Roblox", + "Nevermore", + "Lua", + "particles" + ], + "bugs": { + "url": "https://github.com/Quenty/NevermoreEngine/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/Quenty/NevermoreEngine.git", + "directory": "src/particles/" + }, + "funding": { + "type": "patreon", + "url": "https://www.patreon.com/quenty" + }, + "license": "MIT", + "contributors": [ + "Quenty" + ], + "dependencies": { + "@quenty/loader": "file:../loader", + "@quenty/numbersequenceutils": "file:../numbersequenceutils" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/src/particles/src/Shared/ParticleEmitterUtils.lua b/src/particles/src/Shared/ParticleEmitterUtils.lua new file mode 100644 index 0000000000..3be8e7edb2 --- /dev/null +++ b/src/particles/src/Shared/ParticleEmitterUtils.lua @@ -0,0 +1,37 @@ +--[=[ + @class ParticleEmitterUtils +]=] + +local require = require(script.Parent.loader).load(script) + +local NumberSequenceUtils = require("NumberSequenceUtils") + +local ParticleEmitterUtils = {} + +function ParticleEmitterUtils.scaleSize(adornee, scale) + assert(typeof(adornee) == "Instance", "Bad adornee") + + for _, particleEmitter in pairs(ParticleEmitterUtils.getParticleEmitters(adornee)) do + particleEmitter.Size = NumberSequenceUtils.scale(particleEmitter.Size, scale) + end +end + +function ParticleEmitterUtils.getParticleEmitters(adornee) + assert(typeof(adornee) == "Instance", "Bad adornee") + + local emitters = {} + + if adornee:IsA("ParticleEmitter") then + table.insert(emitters, adornee) + end + + for _, particleEmitter in pairs(adornee:GetDescendants()) do + if particleEmitter:IsA("ParticleEmitter") then + table.insert(emitters, particleEmitter) + end + end + + return emitters +end + +return ParticleEmitterUtils \ No newline at end of file diff --git a/src/particles/src/node_modules.project.json b/src/particles/src/node_modules.project.json new file mode 100644 index 0000000000..46233dac4f --- /dev/null +++ b/src/particles/src/node_modules.project.json @@ -0,0 +1,7 @@ +{ + "name": "node_modules", + "globIgnorePaths": [ "**/.package-lock.json" ], + "tree": { + "$path": { "optional": "../node_modules" } + } +} \ No newline at end of file diff --git a/src/particles/test/default.project.json b/src/particles/test/default.project.json new file mode 100644 index 0000000000..325ab3b8ab --- /dev/null +++ b/src/particles/test/default.project.json @@ -0,0 +1,11 @@ +{ + "name": "ParticlesTest", + "tree": { + "$className": "DataModel", + "ServerScriptService": { + "particles": { + "$path": ".." + } + } + } +} \ No newline at end of file diff --git a/src/ragdoll/src/Server/Classes/Ragdollable.lua b/src/ragdoll/src/Server/Classes/Ragdollable.lua index 85990be4d7..c75af2a800 100644 --- a/src/ragdoll/src/Server/Classes/Ragdollable.lua +++ b/src/ragdoll/src/Server/Classes/Ragdollable.lua @@ -8,7 +8,7 @@ local require = require(script.Parent.loader).load(script) local BaseObject = require("BaseObject") local Maid = require("Maid") -local Motor6DBindersServer = require("Motor6DBindersServer") +local Motor6DStackHumanoid = require("Motor6DStackHumanoid") local PlayerHumanoidBinder = require("PlayerHumanoidBinder") local Ragdoll = require("Ragdoll") local RagdollAdditionalAttachmentUtils = require("RagdollAdditionalAttachmentUtils") @@ -33,9 +33,8 @@ function Ragdollable.new(humanoid, serviceBag) self._serviceBag = assert(serviceBag, "No serviceBag") self._ragdollBinder = self._serviceBag:GetService(Ragdoll) - self._motor6DBindersServer = self._serviceBag:GetService(Motor6DBindersServer) - self._motor6DBindersServer.Motor6DStackHumanoid:Bind(self._obj) + Motor6DStackHumanoid:Tag(self._obj) -- Ensure predefined physics rig immediatelly on the server. -- We do this so during replication loop-back there's no chance of death. diff --git a/src/receiptprocessing/package.json b/src/receiptprocessing/package.json index 2995079bdc..4556fc6d60 100644 --- a/src/receiptprocessing/package.json +++ b/src/receiptprocessing/package.json @@ -29,6 +29,7 @@ "@quenty/loader": "file:../loader", "@quenty/maid": "file:../maid", "@quenty/promise": "file:../promise", + "@quenty/rx": "file:../rx", "@quenty/servicebag": "file:../servicebag", "@quenty/signal": "file:../signal" }, diff --git a/src/receiptprocessing/src/Server/ReceiptProcessingService.lua b/src/receiptprocessing/src/Server/ReceiptProcessingService.lua index 8974788850..60292e649b 100644 --- a/src/receiptprocessing/src/Server/ReceiptProcessingService.lua +++ b/src/receiptprocessing/src/Server/ReceiptProcessingService.lua @@ -12,6 +12,8 @@ local Promise = require("Promise") local EnumUtils = require("EnumUtils") local Signal = require("Signal") local Maid = require("Maid") +local ObservableSubscriptionTable = require("ObservableSubscriptionTable") +local ValueObject = require("ValueObject") local ReceiptProcessingService = {} ReceiptProcessingService.ServiceName = "ReceiptProcessingService" @@ -21,11 +23,11 @@ function ReceiptProcessingService:Init(serviceBag) self._serviceBag = assert(serviceBag, "No serviceBag") self._maid = Maid.new() - self.ReceiptCreated = Signal.new() -- :Fire(receiptInfo) - self._maid:GiveTask(self.ReceiptCreated) + self.ReceiptCreated = self._maid:Add(Signal.new()) -- :Fire(receiptInfo) + self.ReceiptProcessed = self._maid:Add(Signal.new()) -- :Fire(receiptInfo, productPurchaseDecision) - self.ReceiptProcessed = Signal.new() -- :Fire(receiptInfo, productPurchaseDecision) - self._maid:GiveTask(self.ReceiptProcessed) + self._receiptProcessedForUserId = self._maid:Add(ObservableSubscriptionTable.new()) -- :Fire(receiptInfo, productPurchaseDecision) + self._defaultDecision = self._maid:Add(ValueObject.new(Enum.ProductPurchaseDecision.PurchaseGranted, "EnumItem")) self._processors = {} end @@ -36,6 +38,39 @@ function ReceiptProcessingService:Start() end end +--[=[ + Sets the default purchase decision in case you want more control + @param productPurchaseDecision ProductPurchaseDecision +]=] +function ReceiptProcessingService:SetDefaultPurchaseDecision(productPurchaseDecision) + assert(EnumUtils.isOfType(Enum.ProductPurchaseDecision, productPurchaseDecision), "Bad productPurchaseDecision") + + self._defaultDecision.Value = productPurchaseDecision +end + +--[=[ + Observes receipt by player + + @param player Player + @return Observable +]=] +function ReceiptProcessingService:ObserveReceiptProcessedForPlayer(player) + assert(typeof(player) == "Instance" and player:IsA("Player"), "Bad player") + + return self:ObserveReceiptProcessedForUserId(player.UserId) +end +--[=[ + Observes receipt by userId + + @param userId number + @return Observable +]=] +function ReceiptProcessingService:ObserveReceiptProcessedForUserId(userId) + assert(type(userId) == "number", "Bad userId") + + return self._receiptProcessedForUserId:Observe(userId) +end + --[=[ Registers a new receipt processor. This works exactly like a normal receipt processor except it will also take a Promise as a result (of which an error). @@ -98,7 +133,7 @@ function ReceiptProcessingService:_handleProcessReceiptAsync(receiptInfo) end if EnumUtils.isOfType(Enum.ProductPurchaseDecision, result) then - self.ReceiptProcessed:Fire(receiptInfo, result) + self:_fireProcessed(receiptInfo, result) return result elseif result == nil then continue @@ -108,8 +143,20 @@ function ReceiptProcessingService:_handleProcessReceiptAsync(receiptInfo) end -- Retry in the future - self.ReceiptProcessed:Fire(receiptInfo, Enum.ProductPurchaseDecision.NotProcessedYet) - return Enum.ProductPurchaseDecision.NotProcessedYet + self:_fireProcessed(receiptInfo, self._defaultDecision.Value) + return self._defaultDecision.Value +end + +function ReceiptProcessingService:_fireProcessed(receiptInfo, productPurchaseDecision) + assert(EnumUtils.isOfType(Enum.ProductPurchaseDecision, productPurchaseDecision), "Bad productPurchaseDecision") + + self.ReceiptProcessed:Fire(receiptInfo, productPurchaseDecision) + + if type(receiptInfo.PlayerId) == "number" then + self._receiptProcessedForUserId:Fire(receiptInfo.PlayerId, receiptInfo, productPurchaseDecision) + else + warn("[ReceiptProcessingService._fireProcessed] - No receiptInfo.PlayerId") + end end function ReceiptProcessingService:Destroy() diff --git a/src/rx/src/Shared/Rx.lua b/src/rx/src/Shared/Rx.lua index 4459f9e9e1..1a5a71d673 100644 --- a/src/rx/src/Shared/Rx.lua +++ b/src/rx/src/Shared/Rx.lua @@ -221,7 +221,7 @@ end @return Observable ]=] function Rx.fromPromise(promise) - assert(Promise.isPromise(promise)) + assert(Promise.isPromise(promise), "Bad promise") return Observable.new(function(sub) if promise:IsFulfilled() then diff --git a/src/voicechat/README.md b/src/voicechat/README.md new file mode 100644 index 0000000000..a5d78e54fe --- /dev/null +++ b/src/voicechat/README.md @@ -0,0 +1,23 @@ +## VoiceChat + +
+ + Documentation status + + + Discord + + + Build and release status + +
+ +Utility methods for voice chat + +
View docs →
+ +## Installation + +``` +npm install @quenty/voicechat --save +``` diff --git a/src/voicechat/default.project.json b/src/voicechat/default.project.json new file mode 100644 index 0000000000..9a8db038e1 --- /dev/null +++ b/src/voicechat/default.project.json @@ -0,0 +1,6 @@ +{ + "name": "voicechat", + "tree": { + "$path": "src" + } +} diff --git a/src/voicechat/package.json b/src/voicechat/package.json new file mode 100644 index 0000000000..3b7461b883 --- /dev/null +++ b/src/voicechat/package.json @@ -0,0 +1,34 @@ +{ + "name": "@quenty/voicechat", + "version": "1.0.0", + "description": "Utility methods for voice chat", + "keywords": [ + "Roblox", + "Nevermore", + "Lua", + "voicechat" + ], + "bugs": { + "url": "https://github.com/Quenty/NevermoreEngine/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/Quenty/NevermoreEngine.git", + "directory": "src/voicechat/" + }, + "funding": { + "type": "patreon", + "url": "https://www.patreon.com/quenty" + }, + "license": "MIT", + "contributors": [ + "Quenty" + ], + "dependencies": { + "@quenty/loader": "file:../loader", + "@quenty/promise": "file:../promise" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/src/voicechat/src/Shared/VoiceChatUtils.lua b/src/voicechat/src/Shared/VoiceChatUtils.lua new file mode 100644 index 0000000000..20e20f22e9 --- /dev/null +++ b/src/voicechat/src/Shared/VoiceChatUtils.lua @@ -0,0 +1,52 @@ +--[=[ + @class VoiceChatUtils +]=] + +local require = require(script.Parent.loader).load(script) + +local VoiceChatService = game:GetService("VoiceChatService") + +local Promise = require("Promise") + +local VoiceChatUtils = {} + +--[=[ + Reports whether voice chat is enabled + + @param player Player + @return Promise +]=] +function VoiceChatUtils.promiseIsVoiceEnabledForPlayer(player) + assert(typeof(player) == "Instance" and player:IsA("Player"), "Bad userId") + + return VoiceChatUtils.promiseIsVoiceEnabledForUserId(player.UserId) +end + +--[=[ + Wraps whether voice chat is enabled + + @param userId number + @return Promise +]=] +function VoiceChatUtils.promiseIsVoiceEnabledForUserId(userId) + assert(type(userId) == "number", "Bad userId") + + return Promise.spawn(function(resolve, reject) + local result + local ok, err = pcall(function() + result = VoiceChatService:IsVoiceEnabledForUserIdAsync(userId) + end) + + if not ok then + warn(err) + return reject(err) + end + if type(result) ~= "boolean" then + return reject("Result was not a boolean") + end + + return resolve(result) + end) +end + +return VoiceChatUtils \ No newline at end of file diff --git a/src/voicechat/src/node_modules.project.json b/src/voicechat/src/node_modules.project.json new file mode 100644 index 0000000000..46233dac4f --- /dev/null +++ b/src/voicechat/src/node_modules.project.json @@ -0,0 +1,7 @@ +{ + "name": "node_modules", + "globIgnorePaths": [ "**/.package-lock.json" ], + "tree": { + "$path": { "optional": "../node_modules" } + } +} \ No newline at end of file diff --git a/src/voicechat/test/default.project.json b/src/voicechat/test/default.project.json new file mode 100644 index 0000000000..3c218e9892 --- /dev/null +++ b/src/voicechat/test/default.project.json @@ -0,0 +1,11 @@ +{ + "name": "VoiceChatTest", + "tree": { + "$className": "DataModel", + "ServerScriptService": { + "voicechat": { + "$path": ".." + } + } + } +} \ No newline at end of file