@quenty/tie 10.6.0 → 10.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +4 -0
  3. package/package.json +16 -15
  4. package/src/Shared/Members/Methods/TieMethodDefinition.lua +44 -0
  5. package/src/Shared/{Implementation → Members/Methods}/TieMethodImplementation.lua +4 -5
  6. package/src/Shared/Members/Methods/TieMethodInterfaceUtils.lua +58 -0
  7. package/src/Shared/{Interface → Members/Properties}/TiePropertyChangedSignalConnection.lua +1 -1
  8. package/src/Shared/Members/Properties/TiePropertyDefinition.lua +58 -0
  9. package/src/Shared/{Interface → Members/Properties}/TiePropertyInterface.lua +42 -59
  10. package/src/Shared/Members/Signals/TieSignalConnection.lua +63 -0
  11. package/src/Shared/Members/Signals/TieSignalDefinition.lua +38 -0
  12. package/src/Shared/{Implementation → Members/Signals}/TieSignalImplementation.lua +29 -15
  13. package/src/Shared/Members/Signals/TieSignalInterface.lua +90 -0
  14. package/src/Shared/Members/TieMemberDefinition.lua +104 -0
  15. package/src/Shared/Members/TieMemberInterface.lua +94 -0
  16. package/src/Shared/Realms/TieRealmUtils.lua +41 -0
  17. package/src/Shared/{Definition/Types → Realms}/TieRealms.lua +3 -4
  18. package/src/Shared/Services/TieRealmService.lua +31 -0
  19. package/src/Shared/TieDefinition.lua +708 -0
  20. package/src/Shared/TieImplementation.lua +167 -0
  21. package/src/Shared/TieInterface.lua +129 -0
  22. package/src/Shared/{Encoding → Utils}/TieUtils.lua +4 -1
  23. package/test/modules/Server/Action/Action.lua +4 -7
  24. package/test/modules/Server/Door.lua +5 -10
  25. package/test/scripts/Server/ServerMain.server.lua +3 -3
  26. package/src/Shared/Definition/TieDefinition.lua +0 -507
  27. package/src/Shared/Definition/TieMethodDefinition.lua +0 -61
  28. package/src/Shared/Definition/TiePropertyDefinition.lua +0 -64
  29. package/src/Shared/Definition/TieSignalDefinition.lua +0 -59
  30. package/src/Shared/Definition/Types/TieRealmUtils.lua +0 -60
  31. package/src/Shared/Implementation/TieImplementation.lua +0 -129
  32. package/src/Shared/Interface/TieInterface.lua +0 -122
  33. package/src/Shared/Interface/TieInterfaceUtils.lua +0 -70
  34. package/src/Shared/Interface/TieMethodInterfaceUtils.lua +0 -43
  35. package/src/Shared/Interface/TieSignalConnection.lua +0 -89
  36. package/src/Shared/Interface/TieSignalInterface.lua +0 -61
  37. /package/src/Shared/{Implementation → Members/Properties}/TiePropertyImplementation.lua +0 -0
  38. /package/src/Shared/{Implementation → Members/Properties}/TiePropertyImplementationUtils.lua +0 -0
@@ -7,28 +7,22 @@ local require = require(script.Parent.loader).load(script)
7
7
  local BaseObject = require("BaseObject")
8
8
  local TieUtils = require("TieUtils")
9
9
  local Maid = require("Maid")
10
+ local Tuple = require("Tuple")
10
11
 
11
12
  local TieSignalImplementation = setmetatable({}, BaseObject)
12
13
  TieSignalImplementation.ClassName = "TieSignalImplementation"
13
14
  TieSignalImplementation.__index = TieSignalImplementation
14
15
 
15
- function TieSignalImplementation.new(memberDefinition, folder, initialValue)
16
+ function TieSignalImplementation.new(memberDefinition, implParent, initialValue)
16
17
  local self = setmetatable(BaseObject.new(), TieSignalImplementation)
17
18
 
18
19
  self._memberDefinition = assert(memberDefinition, "No memberDefinition")
19
- self._folder = assert(folder, "No folder")
20
+ self._implParent = assert(implParent, "No implParent")
20
21
 
21
- self._bindableEvent = Instance.new("BindableEvent")
22
+ self._bindableEvent = self._maid:Add(Instance.new("BindableEvent"))
22
23
  self._bindableEvent.Archivable = false
23
24
  self._bindableEvent.Name = memberDefinition:GetMemberName()
24
- self._bindableEvent.Parent = self._folder
25
- self._maid:GiveTask(self._bindableEvent)
26
-
27
- -- Abuse the fact that the first signal connected is the first
28
- -- signal to fire!
29
- self._maid:GiveTask(self._bindableEvent.Event:Connect(function()
30
- self._thisIsUsFiring = false
31
- end))
25
+ self._bindableEvent.Parent = self._implParent
32
26
 
33
27
  self:SetImplementation(initialValue)
34
28
 
@@ -39,16 +33,36 @@ function TieSignalImplementation:SetImplementation(signal)
39
33
  local maid = Maid.new()
40
34
 
41
35
  if type(signal) == "table" then
36
+ -- Prevent re-entrance from stuff fired from ourselves when forwarding events in either direction
37
+ local signalFiredArgs = {}
38
+ local bindableEventFiredArgs = {}
39
+
42
40
  maid:GiveTask(signal:Connect(function(...)
43
- self._thisIsUsFiring = true
41
+ local args = Tuple.new(...)
42
+ for pendingArgs, _ in pairs(signalFiredArgs) do
43
+ if pendingArgs == args then
44
+ -- Remove from queue
45
+ signalFiredArgs[pendingArgs] = nil
46
+ return
47
+ end
48
+ end
49
+
50
+ bindableEventFiredArgs[args] = true
44
51
  self._bindableEvent:Fire(TieUtils.encode(...))
45
52
  end))
46
53
 
47
- -- TODO: Listen to the event and fire off our own event (if we aren't the source).
48
54
  maid:GiveTask(self._bindableEvent.Event:Connect(function(...)
49
- if not self._thisIsUsFiring then
50
- signal:Fire(TieUtils.decode(...))
55
+ local args = Tuple.new(TieUtils.decode(...))
56
+ for pendingArgs, _ in pairs(bindableEventFiredArgs) do
57
+ if pendingArgs == args then
58
+ -- Remove from queue
59
+ bindableEventFiredArgs[pendingArgs] = nil
60
+ return
61
+ end
51
62
  end
63
+
64
+ signalFiredArgs[args] = true
65
+ signal:Fire(args:Unpack())
52
66
  end))
53
67
  end
54
68
 
@@ -0,0 +1,90 @@
1
+ --[=[
2
+ Interface that mirrors [Signal] API
3
+
4
+ @class TieSignalInterface
5
+ ]=]
6
+
7
+ local require = require(script.Parent.loader).load(script)
8
+
9
+ local TieMemberInterface = require("TieMemberInterface")
10
+ local TieSignalConnection = require("TieSignalConnection")
11
+ local TieUtils = require("TieUtils")
12
+ local RxBrioUtils = require("RxBrioUtils")
13
+ local RxInstanceUtils = require("RxInstanceUtils")
14
+ local TieRealmUtils = require("TieRealmUtils")
15
+
16
+ local TieSignalInterface = setmetatable({}, TieMemberInterface)
17
+ TieSignalInterface.ClassName = "TieSignalInterface"
18
+ TieSignalInterface.__index = TieSignalInterface
19
+
20
+ function TieSignalInterface.new(implParent, adornee, memberDefinition, interfaceTieRealm)
21
+ assert(TieRealmUtils.isTieRealm(interfaceTieRealm), "Bad interfaceTieRealm")
22
+
23
+ local self = setmetatable(TieMemberInterface.new(implParent, adornee, memberDefinition, interfaceTieRealm), TieSignalInterface)
24
+
25
+ return self
26
+ end
27
+
28
+ --[=[
29
+ Fires the signal
30
+
31
+ @param ... T
32
+ ]=]
33
+ function TieSignalInterface:Fire(...)
34
+ local bindableEvent = self:_getBindableEvent()
35
+ if not bindableEvent then
36
+ warn(string.format("[TieSignalInterface] - No bindableEvent for %q", self._memberDefinition:GetMemberName()))
37
+ end
38
+
39
+ bindableEvent:Fire(TieUtils.encode(...))
40
+ end
41
+
42
+ --[=[
43
+ Connects like an RBXSignalConnection
44
+
45
+ @param callback (T...) -> ()
46
+ @return TieSignalConnection
47
+ ]=]
48
+ function TieSignalInterface:Connect(callback)
49
+ assert(type(callback) == "function", "Bad callback")
50
+
51
+ return TieSignalConnection.new(self, callback)
52
+ end
53
+
54
+ function TieSignalInterface:Wait()
55
+ -- TODO: Implement
56
+ error("Not implemented")
57
+ end
58
+
59
+ function TieSignalInterface:Once(_callback)
60
+ -- TODO: Implement
61
+ error("Not implemented")
62
+ end
63
+
64
+ function TieSignalInterface:ObserveBindableEventBrio()
65
+ local name = self._memberDefinition:GetMemberName()
66
+
67
+ return self:ObserveImplParentBrio():Pipe({
68
+ RxBrioUtils.switchMapBrio(function(implParent)
69
+ return RxInstanceUtils.observeLastNamedChildBrio(implParent, "BindableEvent", name)
70
+ end);
71
+ RxBrioUtils.onlyLastBrioSurvives();
72
+ })
73
+
74
+ end
75
+
76
+ function TieSignalInterface:_getBindableEvent()
77
+ local implParent = self:GetImplParent()
78
+ if not implParent then
79
+ return nil
80
+ end
81
+
82
+ local implementation = implParent:FindFirstChild(self._memberDefinition:GetMemberName())
83
+ if implementation and implementation:IsA("BindableEvent") then
84
+ return implementation
85
+ else
86
+ return nil
87
+ end
88
+ end
89
+
90
+ return TieSignalInterface
@@ -0,0 +1,104 @@
1
+ --[=[
2
+ Base class for a member definition/declaration.
3
+
4
+ @class TieMemberDefinition
5
+ ]=]
6
+
7
+ local require = require(script.Parent.loader).load(script)
8
+
9
+ local TieRealmUtils = require("TieRealmUtils")
10
+ local TieRealms = require("TieRealms")
11
+
12
+ local TieMemberDefinition = {}
13
+ TieMemberDefinition.ClassName = "TieMemberDefinition"
14
+ TieMemberDefinition.__index = TieMemberDefinition
15
+
16
+ function TieMemberDefinition.new(tieDefinition, memberName, memberTieRealm)
17
+ assert(TieRealmUtils.isTieRealm(memberTieRealm), "Bad memberTieRealm")
18
+
19
+ local self = setmetatable({}, TieMemberDefinition)
20
+
21
+ self._tieDefinition = assert(tieDefinition, "No tieDefinition")
22
+ self._memberName = assert(memberName, "Bad memberName")
23
+ self._memberTieRealm = assert(memberTieRealm, "No memberTieRealm")
24
+
25
+ return self
26
+ end
27
+
28
+ function TieMemberDefinition:Implement()
29
+ error("Not implemented")
30
+ end
31
+
32
+ function TieMemberDefinition:GetInterface()
33
+ error("Not implemented")
34
+ end
35
+
36
+ function TieMemberDefinition:GetFriendlyName()
37
+ return string.format("%s.%s", self._tieDefinition:GetName(), self._memberName)
38
+ end
39
+
40
+ function TieMemberDefinition:IsRequiredForInterface(currentRealm)
41
+ assert(TieRealmUtils.isTieRealm(currentRealm), "Bad currentRealm")
42
+
43
+ if self._memberTieRealm == TieRealms.SHARED then
44
+ return true
45
+ elseif currentRealm == TieRealms.SHARED then
46
+ -- Interface can retrieve just the smallest subset... can allow cross sharing unfortunately....
47
+ return false
48
+ else
49
+ return self._memberTieRealm == currentRealm
50
+ end
51
+ end
52
+
53
+ function TieMemberDefinition:IsAllowedOnInterface(currentRealm)
54
+ assert(TieRealmUtils.isTieRealm(currentRealm), "Bad currentRealm")
55
+
56
+ if self._memberTieRealm == TieRealms.SHARED then
57
+ return true
58
+ elseif currentRealm == TieRealms.SHARED then
59
+ -- Allow these to be used on interface if they exist...
60
+ return true
61
+ else
62
+ return self._memberTieRealm == currentRealm
63
+ end
64
+ end
65
+
66
+ function TieMemberDefinition:IsRequiredForImplementation(currentRealm)
67
+ assert(TieRealmUtils.isTieRealm(currentRealm), "Bad currentRealm")
68
+
69
+ if currentRealm == TieRealms.SHARED then
70
+ -- Require both client and server if we're shared to be implemented
71
+ return true
72
+ elseif self._memberTieRealm == TieRealms.SHARED then
73
+ return true
74
+ else
75
+ return self._memberTieRealm == currentRealm
76
+ end
77
+ end
78
+
79
+ function TieMemberDefinition:IsAllowedForImplementation(currentRealm)
80
+ assert(TieRealmUtils.isTieRealm(currentRealm), "Bad currentRealm")
81
+
82
+ if self._memberTieRealm == TieRealms.SHARED then
83
+ return true
84
+ elseif currentRealm == TieRealms.SHARED then
85
+ -- Allowed
86
+ return true
87
+ else
88
+ return self._memberTieRealm == currentRealm
89
+ end
90
+ end
91
+
92
+ function TieMemberDefinition:GetMemberTieRealm()
93
+ return self._memberTieRealm
94
+ end
95
+
96
+ function TieMemberDefinition:GetTieDefinition()
97
+ return self._tieDefinition
98
+ end
99
+
100
+ function TieMemberDefinition:GetMemberName()
101
+ return self._memberName
102
+ end
103
+
104
+ return TieMemberDefinition
@@ -0,0 +1,94 @@
1
+ --[=[
2
+ @class TieMemberInterface
3
+ ]=]
4
+
5
+ local require = require(script.Parent.loader).load(script)
6
+
7
+ local Rx = require("Rx")
8
+ local RxBrioUtils = require("RxBrioUtils")
9
+ local RxInstanceUtils = require("RxInstanceUtils")
10
+ local TieRealmUtils = require("TieRealmUtils")
11
+
12
+ local TieMemberInterface = {}
13
+ TieMemberInterface.ClassName = "TieMemberInterface"
14
+ TieMemberInterface.__index = TieMemberInterface
15
+
16
+ function TieMemberInterface.new(implParent, adornee, memberDefinition, interfaceTieRealm)
17
+ assert(TieRealmUtils.isTieRealm(interfaceTieRealm), "Bad interfaceTieRealm")
18
+
19
+ local self = setmetatable({}, TieMemberInterface)
20
+
21
+ assert(implParent or adornee, "Parent or adornee required")
22
+
23
+ self._implParent = implParent
24
+ self._adornee = adornee
25
+ self._memberDefinition = assert(memberDefinition, "No memberDefinition")
26
+ self._interfaceTieRealm = assert(interfaceTieRealm, "No interfaceTieRealm")
27
+
28
+ self._tieDefinition = self._memberDefinition:GetTieDefinition()
29
+
30
+ return self
31
+ end
32
+
33
+ function TieMemberInterface:GetInterfaceTieRealm()
34
+ return self._interfaceTieRealm
35
+ end
36
+
37
+ function TieMemberInterface:GetImplParent()
38
+ local validContainerNameSet = self._tieDefinition:GetValidContainerNameSet(self._interfaceTieRealm)
39
+
40
+ if self._implParent and self._adornee then
41
+ if self._implParent.Parent == self._adornee and validContainerNameSet[self._implParent.Name] then
42
+ return self._implParent
43
+ else
44
+ return nil
45
+ end
46
+ elseif self._implParent then
47
+ if validContainerNameSet[self._implParent.Name] then
48
+ return self._implParent
49
+ else
50
+ return nil
51
+ end
52
+ elseif self._adornee then
53
+ return self._tieDefinition:GetImplementationParents(self._adornee, self._interfaceTieRealm)
54
+ else
55
+ error("Must have self._implParent or self._adornee")
56
+ end
57
+ end
58
+
59
+ function TieMemberInterface:ObserveImplParentBrio()
60
+ local validContainerNameSet = self._tieDefinition:GetValidContainerNameSet(self._interfaceTieRealm)
61
+
62
+ if self._implParent and self._adornee then
63
+ return Rx.combineLatest({
64
+ Parent = RxInstanceUtils.observeProperty(self._implParent, "Parent");
65
+ Name = RxInstanceUtils.observeProperty(self._implParent, "Name");
66
+ })
67
+ :Pipe({
68
+ Rx.map(function(state)
69
+ if validContainerNameSet[state.Name] and state.Parent == self._adornee then
70
+ return self._implParent
71
+ else
72
+ return nil
73
+ end
74
+ end);
75
+ Rx.distinct();
76
+ RxBrioUtils.toBrio();
77
+ RxBrioUtils.onlyLastBrioSurvives();
78
+ })
79
+ elseif self._implParent then
80
+ return RxInstanceUtils.observePropertyBrio(self._implParent, "Name", function(name)
81
+ return validContainerNameSet[name]
82
+ end):Pipe({
83
+ RxBrioUtils.map(function()
84
+ return self._implParent
85
+ end);
86
+ })
87
+ elseif self._adornee then
88
+ return self._tieDefinition:ObserveValidContainerChildrenBrio(self._adornee, self._interfaceTieRealm)
89
+ else
90
+ error("No self._implParent or adornee")
91
+ end
92
+ end
93
+
94
+ return TieMemberInterface
@@ -0,0 +1,41 @@
1
+ --[=[
2
+ Utility methods to oeprate around [TieRealms]
3
+
4
+ @class TieRealmUtils
5
+ ]=]
6
+
7
+ local require = require(script.Parent.loader).load(script)
8
+
9
+ local RunService = game:GetService("RunService")
10
+ local TieRealms = require("TieRealms")
11
+
12
+ local TieRealmUtils = {}
13
+
14
+ --[=[
15
+ Returns true if the value is a tie realm
16
+
17
+ @param tieRealm any
18
+ @return boolean
19
+ ]=]
20
+ function TieRealmUtils.isTieRealm(tieRealm)
21
+ return tieRealm == TieRealms.CLIENT
22
+ or tieRealm == TieRealms.SERVER
23
+ or tieRealm == TieRealms.SHARED
24
+ end
25
+
26
+ --[=[
27
+ Infers the tie realm from the current RunService state
28
+
29
+ @return TieRealm
30
+ ]=]
31
+ function TieRealmUtils.inferTieRealm()
32
+ if RunService:IsServer() then
33
+ return TieRealms.SERVER
34
+ elseif RunService:IsClient() then
35
+ return TieRealms.CLIENT
36
+ else
37
+ error("[TieRealmUtils.inferTieRealm] - Unknown RunService state")
38
+ end
39
+ end
40
+
41
+ return TieRealmUtils
@@ -7,10 +7,9 @@
7
7
  local require = require(script.Parent.loader).load(script)
8
8
 
9
9
  local Table = require("Table")
10
- local Symbol = require("Symbol")
11
10
 
12
11
  return Table.readonly({
13
- SHARED = Symbol.named("shared");
14
- CLIENT = Symbol.named("client");
15
- SERVER = Symbol.named("server");
12
+ SHARED = "shared";
13
+ CLIENT = "client";
14
+ SERVER = "server";
16
15
  })
@@ -0,0 +1,31 @@
1
+ --[=[
2
+ @class TieRealmService
3
+ ]=]
4
+
5
+ local require = require(script.Parent.loader).load(script)
6
+
7
+ local TieRealmUtils = require("TieRealmUtils")
8
+
9
+ local TieRealmService = {}
10
+ TieRealmService.ServiceName = "TieRealmService"
11
+
12
+ function TieRealmService:Init(serviceBag)
13
+ assert(not self._serviceBag, "Already initialized")
14
+ self._serviceBag = assert(serviceBag, "No serviceBag")
15
+
16
+ if not self._tieRealm then
17
+ self._tieRealm = TieRealmUtils.inferTieRealm()
18
+ end
19
+ end
20
+
21
+ function TieRealmService:SetTieRealm(tieRealm)
22
+ assert(TieRealmUtils.isTieRealm(tieRealm), "Bad tieRealm")
23
+
24
+ self._tieRealm = tieRealm
25
+ end
26
+
27
+ function TieRealmService:GetTieRealm()
28
+ return self._tieRealm
29
+ end
30
+
31
+ return TieRealmService