v4.0.0
https://www.eventbrite.com/e/korge-40-live-release-snacks-networking-tickets-626052298277
Changelog will be updated with highlights after the release event.
Video:
https://www.youtube.com/watch?v=avKhNcVJB5I
Slides (with some updates):
- KorGE 4.0: https://docs.google.com/presentation/d/1BT2XxkSZKk8pMOWOUItWWcLPdHTHi3DUeJLkIr73Bd0/edit?usp=sharing
- KProject: https://docs.google.com/presentation/d/1LCd31z9Ke2_gqtba504R2bnjuGX52OlQwBX58Fg3Mho/edit?usp=sharing
What's Changed
- Make Animator to start automatically by default, without waiting for the next frame, and allowing to configure that behaviour in root and children nodes by @soywiz in #1100
- Support View.renderToBitmap to render the background color too by @soywiz in #1102
- Added CachedContainer functionality + improve _stage lookup by propagating it by @soywiz in #1101
- Fixes ISO8601 parsing with offsets by @soywiz in #1104
- Allow using a pre-allocated array for QOI encoding to avoid allocating a new array for every call. by @Kietyo in #1098
- UI Rework, new TextBlock and rich text support, justify, word wrapping and ellipsis, fixes bounds with filters, add fast material-like circle+rounded rect with shadow rendering based on SDF [Part 1] by @soywiz in #1105
- Supports
korge { androidMsaa = 4 }
to configure antialiased multisampled rendering on Android by @soywiz in #1103 - Initial prebaked MSDF font support + support rendering rich and plain text direclty in RenderContext2D by @soywiz in #1109
- Supports bidirectional Bitmap <-> UIImage conversion by @soywiz in #1108
- Lazy dynamic BitmapFont from VectorFont by @soywiz in #1113
- Start work removing KorUI by starting dropping the KorgeDebugNode interface and moving KorUI to the JVM only KorGE target by @soywiz in #1114
- Support rendering only a specific text range by @soywiz in #1118
- Support native image encoding on native image formats by @soywiz in #1120
- Tests HEIC loading in apple platforms by @soywiz in #917
- Bumps kover agent version to 1.0.688 by @soywiz in #1124
- Try to make KotlinNativeCrossTest abstract to make it compatible with Kotlin 1.8.0-Beta by @soywiz in #1125
- Increase kbignum coverage by @soywiz in #1126
- Add NBuffer to replace MemBuffer, DataBuffer, FBuffer and others now that DataView in JS is fast on all the browsers by @soywiz in #1134
- Major kmem update: unify all Buffers :
FBuffer
,MemBuffer
,DataBuffer
,Fast32Array
... into a new justBuffer
by @soywiz in #1135 - Optimize AG State to be allocation-free & immutable by using inline classes & tightly packed integers, equality comparison, serialization & hashCodes are now trivial by @soywiz in #1137
- Move AG enums and structures outside AG class and add the AG prefix + convert enum class to inline class to get faster performance by @soywiz in #1138
- Change how AGUniformValues work (backported from PR) by @soywiz in #1142
- Remove e2e sample by @soywiz in #1143
- Backported offscreen rendering functionality (create KmlGlContext without a window) by @soywiz in #1144
- Cleanup: Automatically handle some AG objects, and doing preparation work for simplifying AG based on NAG experiments by @soywiz in #1145
- Simplify AG [Part 1] by @soywiz in #1149
- Modify KorgeAndroidView.kt by @MYKIM95 in #1148
- Remove old MicroDynamic, DynamicJvm, DynamicNode, ObjectMapper, Struct, ClassFactory etc. and replace old code by @soywiz in #1150
- Fixes wrong wskey=null in RawSocketWebSocketClient + add test verifying wrong length for wskey by @soywiz in #1152
- Support text input in Linux K/N by @soywiz-otg in #1161
- Add isActive check to animator by @BioRyajenka in #1158
- Cleanup & fix: Removes premultiplied complexity in shaders by @soywiz in #1162
- Call gl.beforeDoRender in AGOpengl by @BioRyajenka in #1163
- Ensure RenderContext.doRender & AGOpengl is calling ag.beforeDoRender by @soywiz in #1165
- Prepare StrReader to be extensible and support streams by @soywiz in #1169
- Support multiple programs/uniforms in BatchBuilder2D by @soywiz in #1151
- Tests and fixes Animator.looped with block nodes by @soywiz in #1173
- Add UniformBlockData & UniformBlockBuffer by @soywiz in #1174
- Adds CharReader & CharReaderFromSyncStream & allows Charset.decode to support streams by @soywiz in #1177
- Add extra Deque variants by @soywiz in #1178
- Support creating SyncStream from a sequence of ByteArray by @soywiz in #1179
- Supports XML streaming by @soywiz in #1180
- Update to Kotlin 1.8.0 by @soywiz in #1123
- Fixes & tests scene transitions parameters by @soywiz in #1184
- Go back to Kotlin 1.7.21 by @soywiz in #1182
- DirectGL on Win32. Hugely reduces OpenGL overhead on windows JVM: Use JNA 5.13 Library.OPTION_SYMBOL_PROVIDER to implement fast direct OpenGL mapping on Windows by @soywiz in #1195
- Fixes DirectGL on win32 and enable it, since it was not enabled by @soywiz in #1198
- Update kotlinx.serialiation and atomicfu versions by @soywiz in #1199
- Update coverage agent to 1.0.692 by @soywiz in #1201
- Move build.gradle*.kts code into buildSrc by @soywiz in #1202
- Do not use kotlin-dsl to not propagate it to the korge gradle plugin by @soywiz in #1203
- Update to Kotlin 1.8 & version substitution by @soywiz in #1204
- Partially fixes GPU rendering by @soywiz in #1205
- Fixes control, alt, shift not working on K/N win32 by @soywiz in #1206
- Fixes YAML special double colon case by @soywiz in #1222
- Fixes characters from being slightly displaced and spaces showing a dot in a LazyBitmapFont by @soywiz in #1223
- Implement array element swapping & rotation by @soywiz in #1227
- Optimized ImageOrientation and make it more useful by @soywiz in #1228
- Support slicing Rectangle by @soywiz in #1229
- Improved RectangleInt and make Bitmap.locking using it by @soywiz in #1230
- Add Bitmap.oriented to transform a bitmap based on an ImageOrientation by @soywiz in #1231
- Simplify how bitmap slices work by @soywiz in #1224
- Moved korim-jpeg to a separate repo using kproject by @soywiz in #1242
- Move LuaK to kproject: https://github.com/korlibs/korge-luak by @soywiz in #1251
- Update with the ability to disable ligatures for TtfFont. by @Kietyo in #1233
- Converted korvi to kproject: https://github.com/korlibs/korge-video by @soywiz in #1254
- Move lipsyncing to kproject: https://github.com/korlibs/korge-lipsync by @soywiz in #1258
- Bump Kotlin to 1.8.10 & serialization to 1.5.0-RC by @soywiz in #1260
- Moved MOD, S3M and XM to korge-audio-formats repo: https://github.com/korlibs/korge-audio-formats by @soywiz in #1261
- Support slight differences between platforms when comparing goldens by @soywiz in #1263
- Bump to Kotlin 1.8.10, unify K/N configuration and disable custom freeCompilerArgs by @soywiz in #1266
- Unify both settings.gradle & buildSrc/build.gradle with repositories defined in gradle/repositories.settings.gradle by @soywiz in #1267
- Do not include BuildVersions in commits by generating them in buildSrc/build.gradle instead in groovy by @soywiz in #1270
- Updating deprecated text to link to other function by @FSaurenbach in #1273
- Add -Xvalue-classes to all targets, and unifies JS building to work with Kotlin 1.8.0 and 1.8.20 due to DSL by @soywiz in #1272
- Move tiled maps to kproject by @soywiz in #1262
- Moved parallax functionality from korge to korge-parallax kproject: https://github.com/korlibs/korge-parallax by @soywiz in #1274
- Linux ALSA support to replace OpenAL on linux by @soywiz in #1277
- Make common ALSA backend for Linux by @soywiz in #1279
- Remove linux openal binaries by @soywiz in #1280
- CoreAudio JVM implementation and remove OpenAL binaries from KorAU by @soywiz in #1281
- Use WaveOut on JVM KorAU on Windows instead of OpenAL & remove openal.dll from KorAU JAR by @soywiz in #1282
- Simplify TileMap now that tiled code with isometric/staggered support is extracted to kproject by @soywiz in #1275
- Re-enable tvos & watchos dummifying stuff in korau/korgw/korge (but the other libraries should be usable) by @soywiz in #1283
- Support dynamic function definition in common code (no invoking in common yet though) by @soywiz in #1285
- Fix userHomeVfs for native targets + test by @beretis in #1127
- Since golden references tested on the JVM might produce different results on different platforms, by @soywiz in #1269
- Enable testing WatchOS & TvOS building on CI by @soywiz in #1287
- Fix ASEprite premultiplication + handle invalid slices by @soywiz in #1288
- Fix deployment targets and remove watchosArm32 since it those devices are already discontinued by @soywiz in #1289
- Remove external sampler functionality by @soywiz in #1290
- Fixes context lost and adds tests for it by @soywiz in #1291
- Ensure that #670 is fixed by @soywiz in #1292
- Korge multiplatform testing library. by @Kietyo in #1244
- Unify Android and JVM source sets on Klock by @soywiz in #1293
- Fixes BigInt.inv + cleanup println in tests by @soywiz in #1296
- Optimize BitArray and re-enable korge-benchmarks + bump to kotlinx.benchmark 0.4.7 by @soywiz in #1298
- Remove println from kds tests, move ignored test to JVM to avoid both disabling it and OOM by @soywiz in #1297
- Allow setting klogger logger levels before running with environment variables ie.
LOG_loggerName=debug
by @soywiz in #1301 - Cleanup korma, korio, korau tests by @soywiz in #1300
- Optimize PNG compression to support Bitmap8 and to use some PNG filters by @soywiz in #1302
- Support posterizaton in screenshot testing to reduce golden sizes by @soywiz in #1303
- Fixes add-opens accessing com.apple.eawt by @soywiz in #1304
- Fixes TileMap not displaying some infinite tiles in some circumstances by @soywiz in #1305
- Initial headless/offscreen rendering tests on the JVM by @soywiz in #1308
- Simplify screenshot testing and make it use offscreen rendering to not be intrusive while running and support CI by @soywiz in #1312
- Try to debug linux egl/mesa with envs by @soywiz in #1314
- Simplify JVM blocking IO usage by @soywiz in #1313
- Unifies copyToTree by @soywiz in #1315
- Try running offscreen rendering tests with LIBGL_ALWAYS_SOFTWARE=true on windows, and enable debugging by @soywiz in #1318
- Replace reference tests with screenshot tests and do some fixes and improvements by @soywiz in #1320
- Fix clipping/scissoring not working as expected on opengl main framebuffer + tests covering both cases by @soywiz in #1322
- Support offscreen main framebuffer and support screenshots using that framebuffer instead to test with screenshots main framebuffer rendering by @soywiz in #1323
- Added features list to information section of README by @eahmed234 in #1325
- Fix & enable macos offscreen/screenshot tests by @soywiz in #1324
- Add screenshot test for SDF functionality by @soywiz in #1327
- Upload
**/build/reports
on failure and make BitmapAsserter write expect/diff/actual to thekorge/build/reports
folder by @soywiz in #1330 - Enable GPU rendering for ShapeView and Circle by default again by @soywiz in #1328
- Fixes GpuShapeView rendering with NON_ZERO windings by @soywiz in #1331
- Initial work on mutable, immutable and value geometry classes by @soywiz in #1332
- Feature/i18n.kproject by @soywiz in #1333
- Prepare Point for MFVC, un-deprecate I/M variants of geometry, and mark immutable versions as experimental for now by @soywiz in #1335
- ASE: Support retrieving hidden layers, storing original ASE index, and MapSubject for ktruth. (attempt 2) by @Kietyo in #1337
- Fix publishing korge-benchmark locally by @soywiz in #1339
- Start making value and mutable korma APIs + join basic extension methods into korma classes by @soywiz in #1340
- Delete deprecations from krypto by @soywiz in #1343
- Fixes XInput mapping by @soywiz in #1349
- Simplify gamepad mapping by @soywiz in #1350
- Simplify, unify & cleanup gamepad backends by @soywiz in #1352
- Get controller name in win32 K/N by @soywiz in #1353
- Tests X11 gamepad + Adds MemorySyncIO by @soywiz in #1354
- Common Win32 gamepad API + test by @soywiz in #1355
- Fix task dependency issue #1345. by @Kietyo in #1348
- Do not require ANDROID_TESTS=true for android connected checks by @soywiz in #1362
- Make Android loaded bitmaps mutable to match the behaviour on the rest of the targets by @soywiz in #1361
- Improve vector support: better AWT bitmap cycle methods & fix sweep on CG & GDI+ & fix radial on GDI+ by @soywiz in #1363
- Simplify batch builders: removed deprecated premultiplied, wrap & colorAdd by @soywiz in #1364
- Moved QR generation to korge-image-formats: by @soywiz in #1365
- Fix task dependency issue #1360 by @Kietyo in #1366
- Move CancellableGroup to korio by @soywiz in #1334
- Support front/back stencil separation & optimize GpuShapeView to benefit from it by @soywiz in #1371
- Make Point and Size inline packing two floats in a Long on concurrent and in a hacky object in JS by @soywiz in #1375
- Cleanup VectorBuilder by @soywiz in #1377
- Fixes masks and backdrop filter by @soywiz in #1380
- Fixes ReloadAgent, display command output errors, and retry on exit after a few seconds by @soywiz in #1381
- Added FastEllipse and FastRoundRect versions using SDF by @soywiz in #1382
- Allow spaces in paths when using reload agent by @soywiz in #1385
- Added replace filter (#955) by @SoftMachina in #1386
- Make FastArrayList much faster on JS by using a mutable array under the hood by @soywiz in #1388
- draft Metal implementation by @ygdrasil-io in #1185
- Fixes metal shader test, simplify metal shader generation and testing and move to common to run tests on JVM that is faster and support assertEquals string comparison by @soywiz in #1390
- Cleanup and start replacing component system with event system by @soywiz in #1391
- Also fixes scene transitions when using the injector by @soywiz in #1392
- Make Margin use fixed short point arithmetic (Add FixedShort). Add tests for Margin & MarginInt by @soywiz in #1394
- Replace Console with Logger almost everywhere by @Maddin-M in #1393
- Use kotlinx.coroutines Dispatchers on K/N for I/O & Resource Decoding, and prepare for kotlinx.coroutines 1.7.x by @soywiz in #1397
- Initial prototype of Uniform Block Buffers (already used in Proj/View matrix & material UI) by @soywiz in #1398
- Unifies KorgeJavaExec and KorgeProcessResources and fixes recursive JVM dependency on itself by @soywiz in #1403
- Added plugin to configure korge libraries in addition to executables by @soywiz in #1404
- Support hotreloading of shared common modules too by @soywiz in #1405
- Enable LinuxArm64 & kotlinx.coroutines 1.7.0-Beta by @soywiz in #1401
- Try org.gradle.parallel by default and configure it on CI by @soywiz in #1370
- Bump gradle to 8.0.2 & Java to 11 by @soywiz in #1299
- Fixes KorTE's JVM ObjectMapper to not redirect to the field if the getter returned null by @soywiz in #1407
- Experiment trying to pack 4 floats without allocating in K/N by @soywiz in #1409
- Added new and simpler way for defining and writing uniform blocks by @soywiz in #1408
- Generate SDF for the default TTF font on the fly by @soywiz in #1412
- Implement some Vector3 & Vector4 operations and use Float4Pack by @soywiz in #1415
- Bump org.jetbrains.intellij.deps:intellij-coverage-agent from 1.0.692 to 1.0.712 by @dependabot in #1420
- Bump com.google.code.gson:gson from 2.8.6 to 2.10.1 by @dependabot in #1416
- Bump com.fasterxml.jackson.module:jackson-module-kotlin from 2.13.3 to 2.14.2 by @dependabot in #1417
- Bump org.jetbrains.dokka:dokka-gradle-plugin from 1.7.20 to 1.8.10 by @dependabot in #1418
- Bump to Kotlin 1.8.20-RC. Upgrade JS Node hack in tests. Go back to gradle 7.6.1 as 8.0.2 + android + intellij is not working properly by @soywiz in #1421
- Moved triangulation, spatial path finding & shape operations to kproject by @soywiz in #1430
- Use setup-java@v3 in github actions to reduce deprecations by @soywiz in #1429
- Update webiny/action-post-run to 3.0.0 by @soywiz in #1431
- Makes ObjectMapper2 interface, make Dynamic2 delegating accessAny to it to support change its behaviour by @soywiz in #1433
- Experiment: pack Matrix in 6 fast bfloat-21-bits without allocating on K/N by @soywiz in #1410
- Make base & max mipmap levels specifiable by @BioRyajenka in #1435
- Run jcmd 0 GC.run on windows before linking tests on CI by @soywiz in #1442
- Matrix4, Quaternion & EulerRotation, Vector2/3/4 improvements & additions by @soywiz in #1443
- Make View and related classes to use Rectangle for getting bounds by @soywiz in #1444
- Fixes GpuShapeView should use premultiplied colors by @soywiz in #1446
- Fixes colMul premultiplied color in FSprites by @soywiz in #1448
- DateTime: Support throwing without adjusting for invalid dates by @soywiz in #1447
- Use uniform buffers in GPUShapeView by @soywiz in #1449
- Fixes AGScissor clipping coordinates by @soywiz in #1450
- Fixes resources copying and windows tool execution in cross targets by @soywiz in #1458
- Separate texture units & samplers from uniforms, and start deprecating AGUniformValues by @soywiz in #1460
- Package renamed by @ygdrasil-io in #1441
- Support auto-installation of box64 when required + fix libX11 loading on Linux by @soywiz in #1459
- Make klogger use java.util.logging.* on the JVM by @soywiz in #1466
- Unify Korge configuration setting by @soywiz in #1465
- rename script applied by @ygdrasil-io in #1467
- Some MRectangle to Rectangle cleanups by @soywiz in #1468
- Fixes gradle plugin id by @soywiz in #1471
- Use immutable variants for types in Uniforms inside UniformBlock by @soywiz in #1470
- Go back to Kotlin 1.8.10 by @soywiz in #1472
- Adds operators to Frequency by @lmvortex4k in #1473
- MRectangle cleanups by @soywiz in #1475
- Some mutable cleanups by @soywiz in #1477
- Downgrade android build gradle version by @soywiz in #1478
- Enable kover only when required by @soywiz in #1479
- Even more mutable cleanups by @soywiz in #1480
- Remove AGValue & AGUniformValues by @soywiz in #1481
- Shape2d improvements and cleanups by @soywiz in #1483
- Further mutable cleanups and MMatrix by @soywiz in #1484
- Update WaveFilter changing crestCount to crestDistance by @soywiz in #1482
- Remove MCircle & MRay as they were already immutable by @soywiz in #1488
- Cache View local bounds by @BioRyajenka in #1487
- Remove related code to the old memory manager by @soywiz in #1489
- Remove MEulerRotation & MQuaternion and use immutable variants in remaining places by @soywiz in #1490
- Make registerStageComponent faster by @BioRyajenka in #1486
- Some Shape2D improvements by @soywiz in #1494
- Remove Modules and simplify KorgeConfig by @soywiz in #1497
- Add Turkish language to Klock Locales by @tekinalper in #1501
- Fixes and refactor android by @soywiz in #1499
- Bump to Kotlin 1.8.20 by @soywiz in #1504
- Support for VAO and UBO when available by @soywiz in #1503
- Fixes CachedContainer & UIScrolalble & Adds FixedSizeCachedContainer clip & builder by @soywiz in #1507
- Fix hotreloading by @soywiz in #1509
- Use Float & Size in Views by @soywiz in #1511
- Update atomicfu by @soywiz in #1513
- Further Double -> Float and x, y to Point by @soywiz in #1515
- Make Bezier immutable and remove IBezier interface by @soywiz in #1516
- Fixes OpenGL contextLost on all targets & checks on the JVM by @soywiz in #1520
- Add runJvm by @soywiz in #1521
- Support VAO on OpenGL MacOS by @soywiz in #1522
- Supports sound pausing/resuming on Android when the apps goes to the background / foreground by @soywiz in #1523
- Fixes back button pressing on Android by @soywiz in #1524
- Implement some DialogInteface methods in iOS by @soywiz in #1525
- Fixes iOS rendering issue by @soywiz in #1527
- Some MPoint -> Point, Double -> Float by @soywiz in #1528
- Huge arrayequal optimization + reduce checkings when uploading buffers to AGBuffer by @soywiz in #1529
- Remove experimental wrapped klock variants by @soywiz in #1508
- Remove old versions for a couple of projects by @soywiz in #1531
- Move korlibs.math.math package to just korlibs.math by @soywiz in #1530
- Move view align methods to korlibs.korge.view.align by @soywiz in #1532
- JVM Views extension mechanism + moved particles to kproject by @soywiz in #1533
- Caches the PGP signatory decoding, that was performed for each artifact, by @soywiz in #1534
- Cleanup MRectangle and MBoundsBuilder by @soywiz in #1535
- Optimize View.localMatrix in K/N by @soywiz in #1537
- Fixes Vector2/Point.isCollinear for fully horizontal points and repeated points by @soywiz in #1539
- Supports android display cutout by @soywiz in #1540
- Moved collisions API to kproject by @soywiz in #1542
- Do not require Korge().start {}. Also keep compatibility with the old parameters windowWidth, bgcolor etc. for now. by @soywiz in #1543
- Align extensions for Float too by @soywiz in #1545
- EmptyShape2d -> EmptyShape2D by @soywiz in #1544
- Update README.md by @soywiz in #1546
- Bump kotlinx-coroutines to 1.7.0-RC by @soywiz in #1547
- Update README.md by @soywiz in #1549
- Update Kotlin to 1.8.21 by @soywiz in #1551
- Support specifying a base offset for the buffer in AGVertexData by @soywiz in #1550
- Reduce the number of texture units to 8 for iOS by @soywiz in #1552
- Support getting AGIndexType.bytesSize and getting it from shader's VarKind by @soywiz in #1553
- Update README.md by @soywiz in #1555
- Fixes Android library release builds being empty by @soywiz in #1556
- Checks latest KorGE version automatically by @soywiz in #1558
- Since bundles are deprecated in favour of kproject, let's remove bundle code from the plugin by @soywiz in #1557
- Fixes AudioDataStream to be able to seek and to restart by @soywiz in #1560
- Fixes android Sound, since the deque writer/reader were in different threads and the datastructure was not thread-safe by @soywiz in #1561
- runJs: correct MIME type for wasm files by @smaugfm in #1562
- Try to fix not using sourceMaps on JS to avoid huge performance bottleneck when running JS by @soywiz in #1563
- Fixes packageJsClosureCompiler task, report errors & apply_input_source_maps by @soywiz in #1564
- Atlas texture generation for *.atlas folder/files, fixes catalog generation and resource processing on all the targets & adds mechanism to add a custom resource processors in the build.gradle by @soywiz in #1565
- runJs: correct MIME type for JavaScript modules by @smaugfm in #1567
- Fixes mipmaps where TEXTURE_BASE/MAX_LEVEL are supported by @soywiz in #1568
- Make Maximize button on macOS cocoa to go fullscreen by @soywiz in #1569
- Fixes empty lines in yaml maps by @soywiz in #1570
- Fixes and tests onStageResized / onEvent(ViewsResizedEvent) by @soywiz in #1571
- Remove old korge gradle plugin code by @soywiz in #1572
- Include resources from dependant projects recursively: fixes kproject dependencies with bundled resources by @soywiz in #1574
- Bump kotlinx.coroutines to 1.7.0 by @soywiz in #1576
- Add starter kits & mascot links by @soywiz in #1573
- fix Starter kits link error for readme by @vlfx in #1578
- Added TileMap.overdrawTiles to be used with LDtk by @soywiz in #1579
- Use an older android build gradle plugin to avoid issues with older IntelliJ versions by @soywiz in #1581
- Add admob kproject link by @soywiz in #1582
- Small fix by @soywiz in #1583
- Fixes remaining resource combination issues for Android and iOS by @soywiz in #1584
- Update korge icon by @soywiz in #1585
New Contributors
- @MYKIM95 made their first contribution in #1148
- @soywiz-otg made their first contribution in #1161
- @BioRyajenka made their first contribution in #1158
- @beretis made their first contribution in #1127
- @eahmed234 made their first contribution in #1325
- @SoftMachina made their first contribution in #1386
- @ygdrasil-io made their first contribution in #1185
- @Maddin-M made their first contribution in #1393
- @dependabot made their first contribution in #1420
- @lmvortex4k made their first contribution in #1473
- @tekinalper made their first contribution in #1501
- @smaugfm made their first contribution in #1562
- @vlfx made their first contribution in #1578
Full Changelog: v3.4.0...v4.0.0