Skip to content

Commit 56cce62

Browse files
Version 1.3.10.1
1 parent df5192e commit 56cce62

File tree

73 files changed

+2068
-508
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+2068
-508
lines changed

build-logic/src/main/kotlin/gg/essential/gradle/RelocatePlugin.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ private fun Project.createAbiValidationTasks(relocateTask: TaskProvider<Relocate
135135
"gg.essential.image",
136136
"gg.essential.key",
137137
"gg.essential.main",
138+
"gg.essential.minecraftauth",
138139
"gg.essential.mixins",
139140
"gg.essential.mod",
140141
"gg.essential.model",

build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ dependencies {
8181
implementation(bundle(project(":clipboard"))!!)
8282
implementation(bundle(project(":utils"))!!)
8383
implementation(bundle(project(":plasmo"))!!)
84+
implementation(bundle(project(":minecraft-auth"))!!)
8485
if (platform.mcVersion >= 11800) {
8586
implementation(bundle(project(":immediatelyfast"))!!)
8687
}

changelog/release-1.3.10.1.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Title: Bug Patch
2+
Summary: Minor bug fixes
3+
4+
## Improvements
5+
- Added an Alternative Menu for accessing Essential screens, accessed via the 'G' key
6+
7+
## Bug Fixes
8+
- Fixed some transparent particles rendering incorrectly
9+
- Fixed purchase confirmation modal not closing after clicking purchase
10+
- Fixed default resource-packs being able to be disabled via drag & dropping
11+
- Fixed IndexOutOfBoundsException when dragging the bottom resource-pack entry in a list
12+
13+
## Compatibility
14+
- Fixed GL_INVALID_OPERATION errors with mods which use NeoForge's `RenderTarget.enableStencil` feature

changelog/release-1.3.10.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
Title: Bug Patch
2-
Summary: Minor bug fixes
1+
Title: Player safety
2+
Summary: Improved player and chat safety
33

44
## Social
55
- Added Community Rules modal, which must be accepted before being able to engage with social features
66
- Added modal to mute or block user after reporting them
77
- Added notification to confirm when a user has been successfully muted
88
- Added modals to communicate why and for how long you were suspended
9+
- Improved reporting modal
910
- Improved Social Menu to show when you cannot talk to a user because they are suspended
1011
- Improved Terms of Use and Privacy Policy modal design
1112

elementa/statev2/src/main/kotlin/gg/essential/gui/elementa/state/v2/impl/basic/impl.kt

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,11 @@ private class Node<T>(
143143
if (impl !is Node<*>) return getUntracked()
144144
if (impl.state == NodeState.Dead) return getUntracked()
145145

146+
// Note: Need to get value before registering the dependent, otherwise if this node is dirty, getUntracked will
147+
// re-evaluate it which marks all dependents as dirty, but this new dependent hasn't seen the old value, so it'd
148+
// be wrong to mark it as dirty.
149+
val value = getUntracked()
150+
146151
val dependency = this
147152
val dependent = impl
148153

@@ -153,7 +158,7 @@ private class Node<T>(
153158
for (edge in if (listA.size < listB.size) listA else listB) {
154159
if (edge.dependency == dependency && edge.dependent == dependent) {
155160
edge.suspended = false // may need to re-enable the edge if it's currently suspended
156-
return getUntracked()
161+
return value
157162
}
158163
}
159164

@@ -166,7 +171,7 @@ private class Node<T>(
166171
// (this is really fast in when there isn't anything to do thanks to the ReferenceQueue)
167172
cleanupStaleReferences()
168173

169-
return getUntracked()
174+
return value
170175
}
171176

172177
override fun getUntracked(): T {
@@ -238,11 +243,15 @@ private class Node<T>(
238243
}
239244
}
240245

241-
if (state == NodeState.Dirty) {
246+
val wasDirty = state == NodeState.Dirty
247+
state = NodeState.Clean
248+
249+
if (wasDirty) {
242250
for (edge in allDependencies) {
243251
edge.suspended = true
244252
}
245253

254+
// Beware: This invocation may throw an exception if user code is faulty! We should handle that correctly.
246255
val newValue = func(this)
247256

248257
if (state == NodeState.Dead) {
@@ -266,8 +275,6 @@ private class Node<T>(
266275
}
267276
}
268277
}
269-
270-
state = NodeState.Clean
271278
}
272279

273280
fun cleanup() {
@@ -339,18 +346,32 @@ private class Update {
339346
return
340347
}
341348

349+
var exception: Throwable? = null
350+
342351
processing = true
343352
try {
344353
var i = 0
345354
while (true) {
346355
val node = queue.getOrNull(i) ?: break
347-
node.update(this)
356+
try {
357+
node.update(this)
358+
} catch (e: Throwable) {
359+
if (exception == null) {
360+
exception = e
361+
} else {
362+
exception.addSuppressed(e)
363+
}
364+
}
348365
i++
349366
}
350367
queue.clear()
351368
} finally {
352369
processing = false
353370
}
371+
372+
if (exception != null) {
373+
throw exception
374+
}
354375
}
355376

356377
companion object {

elementa/statev2/src/main/kotlin/gg/essential/gui/elementa/state/v2/impl/minimal/impl.kt

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,11 @@ private class Node<T>(
135135
if (impl !is Node<*>) return getUntracked()
136136
if (impl.state == NodeState.Dead) return getUntracked()
137137

138+
// Note: Need to get value before registering the dependent, otherwise if this node is dirty, getUntracked will
139+
// re-evaluate it which marks all dependents as dirty, but this new dependent hasn't seen the old value, so it'd
140+
// be wrong to mark it as dirty.
141+
val value = getUntracked()
142+
138143
val dependency = this
139144
val dependent = impl
140145

@@ -145,7 +150,7 @@ private class Node<T>(
145150
for (edge in if (listA.size < listB.size) listA else listB) {
146151
if (edge.dependency == dependency && edge.dependent == dependent) {
147152
edge.suspended = false // may need to re-enable the edge if it's currently suspended
148-
return getUntracked()
153+
return value
149154
}
150155
}
151156

@@ -158,7 +163,7 @@ private class Node<T>(
158163
// (this is really fast in when there isn't anything to do thanks to the ReferenceQueue)
159164
cleanupStaleReferences()
160165

161-
return getUntracked()
166+
return value
162167
}
163168

164169
override fun getUntracked(): T {
@@ -230,11 +235,15 @@ private class Node<T>(
230235
}
231236
}
232237

233-
if (state == NodeState.Dirty) {
238+
val wasDirty = state == NodeState.Dirty
239+
state = NodeState.Clean
240+
241+
if (wasDirty) {
234242
for (edge in allDependencies) {
235243
edge.suspended = true
236244
}
237245

246+
// Beware: This invocation may throw an exception if user code is faulty! We should handle that correctly.
238247
val newValue = func(this)
239248

240249
if (state == NodeState.Dead) {
@@ -258,8 +267,6 @@ private class Node<T>(
258267
}
259268
}
260269
}
261-
262-
state = NodeState.Clean
263270
}
264271

265272
fun cleanup() {
@@ -332,18 +339,32 @@ private class Update {
332339
return
333340
}
334341

342+
var exception: Throwable? = null
343+
335344
processing = true
336345
try {
337346
var i = 0
338347
while (true) {
339348
val node = queue.getOrNull(i) ?: break
340-
node.update(this)
349+
try {
350+
node.update(this)
351+
} catch (e: Throwable) {
352+
if (exception == null) {
353+
exception = e
354+
} else {
355+
exception.addSuppressed(e)
356+
}
357+
}
341358
i++
342359
}
343360
queue.clear()
344361
} finally {
345362
processing = false
346363
}
364+
365+
if (exception != null) {
366+
throw exception
367+
}
347368
}
348369

349370
companion object {

elementa/statev2/src/test/kotlin/gg/essential/gui/elementa/state/v2/EffectTest.kt

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,11 @@
1212
package gg.essential.gui.elementa.state.v2
1313

1414
import gg.essential.elementa.state.v2.ReferenceHolder
15+
import org.junit.jupiter.api.assertThrows
1516
import kotlin.test.Test
1617
import kotlin.test.assertEquals
18+
import kotlin.test.assertNotNull
19+
import kotlin.test.assertNull
1720
import kotlin.time.Duration.Companion.seconds
1821
import kotlin.time.TimeSource
1922

@@ -128,6 +131,24 @@ class EffectTest {
128131
assertEquals(3, ran, "Effect should not have ran, it had been unregistered")
129132
}
130133

134+
@Test
135+
fun testEffectChangingItsOwnDependencies() {
136+
val state = mutableStateOf(1)
137+
138+
val seenValues = mutableListOf<Int>()
139+
140+
val unregister = effect(ReferenceHolder.Weak) {
141+
val value = state()
142+
seenValues.add(value)
143+
144+
state.set(2)
145+
}
146+
147+
assertEquals(listOf(1, 2), seenValues)
148+
149+
unregister()
150+
}
151+
131152
@Test
132153
fun testEffectGarbageCollection() {
133154
val state = mutableStateOf(0)
@@ -167,4 +188,112 @@ class EffectTest {
167188
}
168189
}
169190
}
191+
192+
@Test
193+
fun testEffectThrowingException() {
194+
val state = mutableStateOf(0)
195+
// We'll also throw in a `memo` to check that its internal state isn't corrupted either
196+
val derived = memo { state() }
197+
198+
class TestException : RuntimeException()
199+
200+
var assert1: ((Int?) -> Unit)? = { assertEquals(0, it) }
201+
var throw1: TestException? = null
202+
val unregister1 = effect(ReferenceHolder.Weak) {
203+
assert1.let { func ->
204+
assertNotNull(func, "Unexpected invocation of first effect!")
205+
func(derived())
206+
}
207+
assert1 = null
208+
209+
throw1?.let { throw it }
210+
}
211+
212+
var assert2: ((Int?) -> Unit)? = { assertEquals(0, it) }
213+
var throw2: TestException? = null
214+
val unregister2 = effect(ReferenceHolder.Weak) {
215+
assert2.let { func ->
216+
assertNotNull(func, "Unexpected invocation of second effect!")
217+
func(derived())
218+
}
219+
assert2 = null
220+
221+
throw2?.let { throw it }
222+
}
223+
224+
// Ensure setup is correct
225+
assert1 = { assertEquals(1, it) }
226+
assert2 = { assertEquals(1, it) }
227+
state.set(1)
228+
assertNull(assert1, "First effect was not invoked")
229+
assertNull(assert2, "Second effect was not invoked")
230+
assertEquals(1, derived.getUntracked())
231+
232+
// Have the first effect throw
233+
assert1 = { assertEquals(2, it) }
234+
assert2 = { assertEquals(2, it) }
235+
throw1 = TestException()
236+
assertEquals(throw1, assertThrows<TestException> {
237+
state.set(2)
238+
})
239+
throw1 = null
240+
assertNull(assert1, "First effect was not invoked")
241+
assertNull(assert2, "Second effect was not invoked")
242+
assertEquals(2, derived.getUntracked())
243+
244+
// Ensure future updates are unaffected
245+
assert1 = { assertEquals(3, it) }
246+
assert2 = { assertEquals(3, it) }
247+
state.set(3)
248+
assertNull(assert1, "First effect was not invoked")
249+
assertNull(assert2, "Second effect was not invoked")
250+
assertEquals(3, derived.getUntracked())
251+
252+
// Have the second effect throw
253+
assert1 = { assertEquals(4, it) }
254+
assert2 = { assertEquals(4, it) }
255+
throw2 = TestException()
256+
assertEquals(throw2, assertThrows<TestException> {
257+
state.set(4)
258+
})
259+
throw2 = null
260+
assertNull(assert1, "First effect was not invoked")
261+
assertNull(assert2, "Second effect was not invoked")
262+
assertEquals(4, derived.getUntracked())
263+
264+
// Ensure future updates are unaffected
265+
assert1 = { assertEquals(5, it) }
266+
assert2 = { assertEquals(5, it) }
267+
state.set(5)
268+
assertNull(assert1, "First effect was not invoked")
269+
assertNull(assert2, "Second effect was not invoked")
270+
assertEquals(5, derived.getUntracked())
271+
272+
// Have both effects throw
273+
assert1 = { assertEquals(6, it) }
274+
assert2 = { assertEquals(6, it) }
275+
throw1 = TestException()
276+
throw2 = TestException()
277+
val e = assertThrows<TestException> {
278+
state.set(6)
279+
}
280+
assertEquals(throw1, e)
281+
assertEquals(throw2, throw1.suppressedExceptions[0])
282+
throw1 = null
283+
throw2 = null
284+
assertNull(assert1, "First effect was not invoked")
285+
assertNull(assert2, "Second effect was not invoked")
286+
assertEquals(6, derived.getUntracked())
287+
288+
// Ensure future updates are unaffected
289+
assert1 = { assertEquals(7, it) }
290+
assert2 = { assertEquals(7, it) }
291+
state.set(7)
292+
assertNull(assert1, "First effect was not invoked")
293+
assertNull(assert2, "Second effect was not invoked")
294+
assertEquals(7, derived.getUntracked())
295+
296+
unregister1()
297+
unregister2()
298+
}
170299
}

features.properties

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,2 @@
11
always=true
2-
server_discovery=true
3-
updated_gifting_modal=true
4-
updated_coins_purchase_modal=true
5-
modal_update=true
62
suspensions_and_chat_filtering=true

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ minecraftVersion=11202
1010
# TODO remove once upgrading to Loom 1.10
1111
# fabric-api 1.21.5 was built with Loom 1.10, seems to work well enough in dev with our current 1.7 though
1212
loom.ignoreDependencyLoomVersionValidation=true
13-
version=1.3.10
13+
version=1.3.10.1

0 commit comments

Comments
 (0)