@quenty/friendutils 12.41.0 → 12.42.1
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.
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [12.42.1](https://github.com/Quenty/NevermoreEngine/compare/@quenty/friendutils@12.42.0...@quenty/friendutils@12.42.1) (2026-09-03)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @quenty/friendutils
|
|
9
|
+
|
|
10
|
+
# [12.42.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/friendutils@12.41.0...@quenty/friendutils@12.42.0) (2026-09-02)
|
|
11
|
+
|
|
12
|
+
**Note:** Version bump only for package @quenty/friendutils
|
|
13
|
+
|
|
6
14
|
# [12.41.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/friendutils@12.40.0...@quenty/friendutils@12.41.0) (2026-08-28)
|
|
7
15
|
|
|
8
16
|
**Note:** Version bump only for package @quenty/friendutils
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quenty/friendutils",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.42.1",
|
|
4
4
|
"description": "Utlity functions to help find friends of a user. Also contains utility to make testing in studio easier.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Roblox",
|
|
@@ -29,12 +29,12 @@
|
|
|
29
29
|
],
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@quenty/brio": "14.37.0",
|
|
32
|
-
"@quenty/coreguiutils": "10.
|
|
32
|
+
"@quenty/coreguiutils": "10.26.1",
|
|
33
33
|
"@quenty/loader": "10.11.1",
|
|
34
34
|
"@quenty/maid": "3.12.0",
|
|
35
35
|
"@quenty/nevermore-test-runner": "1.6.0",
|
|
36
36
|
"@quenty/pagesutils": "5.25.0",
|
|
37
|
-
"@quenty/playermock": "1.
|
|
37
|
+
"@quenty/playermock": "1.9.1",
|
|
38
38
|
"@quenty/promise": "10.24.0",
|
|
39
39
|
"@quenty/rx": "13.34.0",
|
|
40
40
|
"@quentystudios/jest-lua": "3.10.0-quenty.2"
|
|
@@ -42,5 +42,5 @@
|
|
|
42
42
|
"publishConfig": {
|
|
43
43
|
"access": "public"
|
|
44
44
|
},
|
|
45
|
-
"gitHead": "
|
|
45
|
+
"gitHead": "76bd184d0b68008d8fbc9e5d07b12f28eeab21f5"
|
|
46
46
|
}
|
|
@@ -118,7 +118,7 @@ function FriendUtils.promiseFriendPages(userId: number): Promise.Promise<FriendP
|
|
|
118
118
|
return Promise.spawn(function(resolve, reject)
|
|
119
119
|
local mockPlayer = PlayerMock.getMockByUserId(userId)
|
|
120
120
|
if mockPlayer ~= nil then
|
|
121
|
-
local friends = PlayerMock.
|
|
121
|
+
local friends = PlayerMock.callMethod(mockPlayer, "Players.GetFriendsAsync", 0)
|
|
122
122
|
return resolve(PagesProxy.new(PagesDatabase.fromPageData({ friends })) :: any)
|
|
123
123
|
end
|
|
124
124
|
|
|
@@ -31,10 +31,10 @@ describe("FriendUtils.promiseAllFriends against a mock", function()
|
|
|
31
31
|
it("resolves the injected friends list", function()
|
|
32
32
|
local player = PlayerMock.new({ UserId = 90041001 })
|
|
33
33
|
player.Parent = Workspace
|
|
34
|
-
PlayerMock.writeLookup(player, "Players.GetFriendsAsync",
|
|
34
|
+
PlayerMock.writeLookup(player, "Players.GetFriendsAsync", {
|
|
35
35
|
makeFriendData(90041002, true),
|
|
36
36
|
makeFriendData(90041003, false),
|
|
37
|
-
})
|
|
37
|
+
}, 0)
|
|
38
38
|
|
|
39
39
|
local outcome, friends = PromiseTestUtils.awaitOutcome(FriendUtils.promiseAllFriends(90041001))
|
|
40
40
|
|
|
@@ -62,11 +62,11 @@ describe("FriendUtils.promiseAllFriends against a mock", function()
|
|
|
62
62
|
it("stops at limitMaxFriends", function()
|
|
63
63
|
local player = PlayerMock.new({ UserId = 90041005 })
|
|
64
64
|
player.Parent = Workspace
|
|
65
|
-
PlayerMock.writeLookup(player, "Players.GetFriendsAsync",
|
|
65
|
+
PlayerMock.writeLookup(player, "Players.GetFriendsAsync", {
|
|
66
66
|
makeFriendData(90041006, true),
|
|
67
67
|
makeFriendData(90041007, true),
|
|
68
68
|
makeFriendData(90041008, true),
|
|
69
|
-
})
|
|
69
|
+
}, 0)
|
|
70
70
|
|
|
71
71
|
local outcome, friends = PromiseTestUtils.awaitOutcome(FriendUtils.promiseAllFriends(90041005, 2))
|
|
72
72
|
|
|
@@ -26,15 +26,29 @@ local function getUserId(player: Player): number
|
|
|
26
26
|
return if PlayerMock.isMock(player) then PlayerMock.read(player, "UserId") else player.UserId
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
+
local function readFriendship(observedPlayer: Player, otherPlayer: Player): boolean
|
|
30
|
+
if PlayerMock.isMock(observedPlayer) then
|
|
31
|
+
return PlayerMock.callMethod(observedPlayer, "Player.IsFriendsWithAsync", getUserId(otherPlayer))
|
|
32
|
+
elseif PlayerMock.isMock(otherPlayer) then
|
|
33
|
+
return PlayerMock.callMethod(otherPlayer, "Player.IsFriendsWithAsync", getUserId(observedPlayer))
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
return observedPlayer:IsFriendsWithAsync(getUserId(otherPlayer))
|
|
37
|
+
end
|
|
38
|
+
|
|
29
39
|
--[=[
|
|
30
40
|
Observe friends in the current server (not including the LocalPlayer!), useful for social GUIs.
|
|
31
41
|
The lifetimes exist for the whole duration another player is a friend and in your server.
|
|
32
42
|
This means if a player is unfriended + friended multiple times per session, they will have emitted multiple friend lifetimes.
|
|
33
43
|
|
|
34
|
-
Works against [PlayerMock] players on either side: mocks in the DataModel are enumerated
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
44
|
+
Works against [PlayerMock] players on either side: mocks in the DataModel are enumerated alongside
|
|
45
|
+
real players, and friendship resolves through the mock's `"Player.IsFriendsWithAsync"` domain. A
|
|
46
|
+
mock local player answers `StarterGui:GetCore` with its own stand-in for the CoreGui events below,
|
|
47
|
+
so a test fires a friending the way the CoreGui does:
|
|
48
|
+
|
|
49
|
+
```lua
|
|
50
|
+
PlayerMock.callMethod(player, "StarterGui.GetCore", "PlayerFriendedEvent"):Fire(otherPlayer)
|
|
51
|
+
```
|
|
38
52
|
|
|
39
53
|
@param player Player?
|
|
40
54
|
@return Observable<Brio<Player>>
|
|
@@ -54,11 +68,6 @@ function RxFriendUtils.observeFriendsInServerAsBrios(player: Player?): Observabl
|
|
|
54
68
|
return Observable.new(function(sub)
|
|
55
69
|
local maid = Maid.new()
|
|
56
70
|
|
|
57
|
-
-- Per-otherPlayer wiring (mock friendship-changed connections). Separate from the brio keyed
|
|
58
|
-
-- under maid[otherPlayer]: an unfriend kills the brio but the wiring must survive so a
|
|
59
|
-
-- re-friend emits a new lifetime. Both die together when the other player leaves.
|
|
60
|
-
local listenMaid = maid:Add(Maid.new())
|
|
61
|
-
|
|
62
71
|
local function handleFriendState(otherPlayer: Player, isFriendsWith: boolean)
|
|
63
72
|
if otherPlayer == Players.LocalPlayer then
|
|
64
73
|
return
|
|
@@ -82,37 +91,9 @@ function RxFriendUtils.observeFriendsInServerAsBrios(player: Player?): Observabl
|
|
|
82
91
|
return
|
|
83
92
|
end
|
|
84
93
|
|
|
85
|
-
-- Friendship is symmetric, so with only one side mocked the injected state lives on
|
|
86
|
-
-- whichever player is the mock; with both mocked, on the observed player.
|
|
87
|
-
local lookupMock: Player? = nil
|
|
88
|
-
local lookupKey: number? = nil
|
|
89
|
-
if PlayerMock.isMock(observedPlayer) then
|
|
90
|
-
lookupMock = observedPlayer
|
|
91
|
-
lookupKey = getUserId(otherPlayer)
|
|
92
|
-
elseif PlayerMock.isMock(otherPlayer) then
|
|
93
|
-
lookupMock = otherPlayer
|
|
94
|
-
lookupKey = getUserId(observedPlayer)
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
if lookupMock ~= nil and lookupKey ~= nil then
|
|
98
|
-
local mock: Player, key: number = lookupMock, lookupKey
|
|
99
|
-
|
|
100
|
-
local function readFriendState()
|
|
101
|
-
handleFriendState(otherPlayer, PlayerMock.readLookup(mock, "Player.IsFriendsWithAsync", key))
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
-- The lookup attribute's changed signal stands in for the CoreGui friended /
|
|
105
|
-
-- unfriended events below, which only the engine can fire.
|
|
106
|
-
listenMaid[otherPlayer] = PlayerMock.getLookupChangedSignal(mock, "Player.IsFriendsWithAsync", key)
|
|
107
|
-
:Connect(readFriendState)
|
|
108
|
-
|
|
109
|
-
readFriendState()
|
|
110
|
-
return
|
|
111
|
-
end
|
|
112
|
-
|
|
113
94
|
local isFriendsWith = false
|
|
114
95
|
local ok = pcall(function()
|
|
115
|
-
isFriendsWith = observedPlayer
|
|
96
|
+
isFriendsWith = readFriendship(observedPlayer, otherPlayer)
|
|
116
97
|
end)
|
|
117
98
|
if not ok then
|
|
118
99
|
warn(
|
|
@@ -132,7 +113,6 @@ function RxFriendUtils.observeFriendsInServerAsBrios(player: Player?): Observabl
|
|
|
132
113
|
end
|
|
133
114
|
|
|
134
115
|
local function handlePlayerRemoving(otherPlayer: Player)
|
|
135
|
-
listenMaid[otherPlayer] = nil
|
|
136
116
|
maid[otherPlayer] = nil
|
|
137
117
|
end
|
|
138
118
|
|
|
@@ -162,7 +142,8 @@ function RxFriendUtils.observeFriendsInServerAsBrios(player: Player?): Observabl
|
|
|
162
142
|
-- https://devforum.roblox.com/t/playerfriendedevent-was-deleted-from-corescripts/696683
|
|
163
143
|
-- So just incase these connections throw, retrieve them off-thread so we don't error out the whole observable.
|
|
164
144
|
-- Only allow this while the game is running too
|
|
165
|
-
|
|
145
|
+
local localPlayer = Players.LocalPlayer or PlayerMock.getMockedLocalPlayer()
|
|
146
|
+
if observedPlayer == localPlayer and (RunService:IsRunning() or PlayerMock.isMock(observedPlayer)) then
|
|
166
147
|
-- Getting the core yields, so the subscription may be cleaned up before it resolves.
|
|
167
148
|
local cancelled = false
|
|
168
149
|
maid:GiveTask(function()
|
|
@@ -32,6 +32,17 @@ describe("RxFriendUtils.observeFriendsInServerAsBrios", function()
|
|
|
32
32
|
return player
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
+
local function makeLocalMock(userId: number): Player
|
|
36
|
+
local player = makeMock(userId)
|
|
37
|
+
maid:GiveTask(PlayerMock.setMockedLocalPlayer(player))
|
|
38
|
+
return player
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
local function fireCoreFriendship(observer: Player, otherPlayer: Player, isFriend: boolean): ()
|
|
42
|
+
local coreName = if isFriend then "PlayerFriendedEvent" else "PlayerUnfriendedEvent"
|
|
43
|
+
PlayerMock.callMethod(observer, "StarterGui.GetCore", coreName):Fire(otherPlayer)
|
|
44
|
+
end
|
|
45
|
+
|
|
35
46
|
local function trackBrios(observer: Player): { Brio.Brio<Player> }
|
|
36
47
|
local emissions: { Brio.Brio<Player> } = {}
|
|
37
48
|
maid:GiveTask(RxFriendUtils.observeFriendsInServerAsBrios(observer):Subscribe(function(brio)
|
|
@@ -43,7 +54,7 @@ describe("RxFriendUtils.observeFriendsInServerAsBrios", function()
|
|
|
43
54
|
it("emits an initial lifetime for a mock that is already a friend", function()
|
|
44
55
|
local observer = makeMock(90051001)
|
|
45
56
|
local friend = makeMock(90051002)
|
|
46
|
-
PlayerMock.writeLookup(observer, "Player.IsFriendsWithAsync",
|
|
57
|
+
PlayerMock.writeLookup(observer, "Player.IsFriendsWithAsync", true, 90051002)
|
|
47
58
|
|
|
48
59
|
local emissions = trackBrios(observer)
|
|
49
60
|
|
|
@@ -61,30 +72,30 @@ describe("RxFriendUtils.observeFriendsInServerAsBrios", function()
|
|
|
61
72
|
expect(#emissions).toBe(0)
|
|
62
73
|
end)
|
|
63
74
|
|
|
64
|
-
it("emits when
|
|
65
|
-
local observer =
|
|
75
|
+
it("emits when the CoreGui reports a friending and kills the lifetime on unfriend", function()
|
|
76
|
+
local observer = makeLocalMock(90051005)
|
|
66
77
|
local friend = makeMock(90051006)
|
|
67
78
|
|
|
68
79
|
local emissions = trackBrios(observer)
|
|
69
80
|
expect(#emissions).toBe(0)
|
|
70
81
|
|
|
71
|
-
|
|
82
|
+
fireCoreFriendship(observer, friend, true)
|
|
72
83
|
expect(#emissions).toBe(1)
|
|
73
84
|
expect(emissions[1]:GetValue()).toBe(friend)
|
|
74
85
|
|
|
75
|
-
|
|
86
|
+
fireCoreFriendship(observer, friend, false)
|
|
76
87
|
expect(emissions[1]:IsDead()).toBe(true)
|
|
77
88
|
end)
|
|
78
89
|
|
|
79
90
|
it("emits a fresh lifetime per re-friend", function()
|
|
80
|
-
local observer =
|
|
81
|
-
makeMock(90051008)
|
|
91
|
+
local observer = makeLocalMock(90051007)
|
|
92
|
+
local friend = makeMock(90051008)
|
|
82
93
|
|
|
83
94
|
local emissions = trackBrios(observer)
|
|
84
95
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
96
|
+
fireCoreFriendship(observer, friend, true)
|
|
97
|
+
fireCoreFriendship(observer, friend, false)
|
|
98
|
+
fireCoreFriendship(observer, friend, true)
|
|
88
99
|
|
|
89
100
|
expect(#emissions).toBe(2)
|
|
90
101
|
expect(emissions[1]:IsDead()).toBe(true)
|
|
@@ -92,22 +103,20 @@ describe("RxFriendUtils.observeFriendsInServerAsBrios", function()
|
|
|
92
103
|
end)
|
|
93
104
|
|
|
94
105
|
it("does not emit a duplicate lifetime for a repeated friended signal", function()
|
|
95
|
-
local observer =
|
|
96
|
-
makeMock(90051010)
|
|
97
|
-
PlayerMock.writeLookup(observer, "Player.IsFriendsWithAsync",
|
|
106
|
+
local observer = makeLocalMock(90051009)
|
|
107
|
+
local friend = makeMock(90051010)
|
|
108
|
+
PlayerMock.writeLookup(observer, "Player.IsFriendsWithAsync", true, 90051010)
|
|
98
109
|
|
|
99
110
|
local emissions = trackBrios(observer)
|
|
100
111
|
|
|
101
|
-
|
|
102
|
-
-- the same state must not open a second lifetime.
|
|
103
|
-
PlayerMock.writeLookup(observer, "Player.IsFriendsWithAsync", 90051010, true)
|
|
112
|
+
fireCoreFriendship(observer, friend, true)
|
|
104
113
|
|
|
105
114
|
expect(#emissions).toBe(1)
|
|
106
115
|
end)
|
|
107
116
|
|
|
108
117
|
it("emits for a friend mock that joins after subscription", function()
|
|
109
118
|
local observer = makeMock(90051011)
|
|
110
|
-
PlayerMock.writeLookup(observer, "Player.IsFriendsWithAsync",
|
|
119
|
+
PlayerMock.writeLookup(observer, "Player.IsFriendsWithAsync", true, 90051012)
|
|
111
120
|
|
|
112
121
|
local emissions = trackBrios(observer)
|
|
113
122
|
expect(#emissions).toBe(0)
|
|
@@ -121,7 +130,7 @@ describe("RxFriendUtils.observeFriendsInServerAsBrios", function()
|
|
|
121
130
|
it("kills the lifetime when the friend mock leaves", function()
|
|
122
131
|
local observer = makeMock(90051013)
|
|
123
132
|
local friend = makeMock(90051014)
|
|
124
|
-
PlayerMock.writeLookup(observer, "Player.IsFriendsWithAsync",
|
|
133
|
+
PlayerMock.writeLookup(observer, "Player.IsFriendsWithAsync", true, 90051014)
|
|
125
134
|
|
|
126
135
|
local emissions = trackBrios(observer)
|
|
127
136
|
expect(#emissions).toBe(1)
|