Skip to content

Commit

Permalink
Merge branch 'refactor/skeletal'
Browse files Browse the repository at this point in the history
# Conflicts:
#	src/integration-test/kotlin/de/bixilon/minosoft/MinosoftSIT.kt
#	src/integration-test/kotlin/de/bixilon/minosoft/data/physics/PhysicsTestUtil.kt
#	src/integration-test/kotlin/de/bixilon/minosoft/protocol/network/connection/play/ConnectionTestUtil.kt
#	src/main/java/de/bixilon/minosoft/data/registries/blocks/properties/BlockProperties.kt
#	src/main/java/de/bixilon/minosoft/data/registries/blocks/types/pixlyzer/PixLyzerBlock.kt
#	src/main/java/de/bixilon/minosoft/gui/rendering/models/block/state/baked/BakedFace.kt
#	src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/dynamic/DynamicTextureListener.kt
#	src/main/java/de/bixilon/minosoft/protocol/protocol/ProtocolVersions.kt
#	src/main/java/de/bixilon/minosoft/util/collections/floats/FragmentedArrayFloatList.kt
#	src/main/resources/assets/minosoft/mapping/assets_properties.json
#	src/main/resources/assets/minosoft/mapping/versions.json
  • Loading branch information
Bixilon committed Nov 18, 2023
2 parents 406819f + 5ba3ec2 commit b27c9a5
Show file tree
Hide file tree
Showing 680 changed files with 14,828 additions and 7,341 deletions.
6 changes: 3 additions & 3 deletions ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Minosoft is an open source minecraft client, written from scratch in kotlin (and
### Features

- Blocks
- Entities (hitboxes and players for now)
- Entities
- Block entities (e.g. signs, chests)
- HUD and GUI (inventory, menus, ...)
- Particles
Expand All @@ -61,8 +61,8 @@ The Hypixel skyblock hub (don't try to make such a screenshot)
![Rendering](doc/img/afk_pool.png)
AFK Pooling, Hit boxes, particles, ...

![Rendering](doc/img/rendering1.png)
Lighting demo.
![Hypixel Lobby](doc/img/hypixel_lobby.png)
Lobby of hypixel.net with entities.

![Rendering](doc/img/sunset.png)
A beautiful sunset
Expand Down
29 changes: 19 additions & 10 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ import java.nio.charset.StandardCharsets


plugins {
kotlin("jvm") version "1.9.10"
kotlin("jvm") version "1.9.20"
`jvm-test-suite`
application
id("org.ajoberstar.grgit.service") version "5.2.0"
id("org.ajoberstar.grgit.service") version "5.2.1"
id("com.github.ben-manes.versions") version "0.49.0"
}

Expand Down Expand Up @@ -129,7 +129,8 @@ when (os) {
/*
Architectures.ARM -> {
lwjglNatives += "-arm64"
zstdNatives += "-amd64" // TODO: Windows on arm is not yet supported: https://github.com/luben/zstd-jni/issues/277
zstdNatives += "-amd64"
// TODO: javafx for Windows on arm is not yet supported: https://github.com/luben/zstd-jni/issues/277
}
*/

Expand All @@ -152,7 +153,7 @@ testing {
dependencies {
implementation(project())
implementation("de.bixilon:kutil:$kutilVersion")
implementation("org.jetbrains.kotlin:kotlin-test:1.9.10")
implementation("org.jetbrains.kotlin:kotlin-test:1.9.20")
}

targets {
Expand Down Expand Up @@ -228,7 +229,8 @@ testing {
options {
val options = this as TestNGOptions
options.preserveOrder = true
// options.excludeGroups("models", "mesher", "chunk", "input", "font", "command", "registry", "biome", "version", "fluid", "world", "raycasting", "pixlyzer", "item", "block", "physics", "light", "packet", "container", "item_stack", "signature", "private_key", "interaction", "item_digging", "chunk_renderer", "rendering")
// options.excludeGroups("models", "mesher", "chunk", "input", "font", "command", "registry", "biome", "version", "fluid", "world", "raycasting", "pixlyzer", "item", "block", "physics", "light", "packet", "container", "item_stack", "signature", "private_key", "interaction", "item_digging", "chunk_renderer", "rendering", "texture", "atlas", "gui")
// options.excludeGroups("models", "chunk", "input", "font", "command", "registry", "biome", "version", "fluid", "world", "raycasting", "pixlyzer", "item", "physics", "light", "packet", "container", "item_stack", "signature", "private_key", "interaction", "item_digging", "chunk_renderer", "texture", "atlas", "gui")
}
}
}
Expand Down Expand Up @@ -332,13 +334,13 @@ fun DependencyHandler.lwjgl(name: String? = null) {

dependencies {
implementation("org.slf4j", "slf4j-api", "2.0.9")
implementation("com.google.guava", "guava", "32.1.2-jre")
implementation("com.google.guava", "guava", "32.1.3-jre")
implementation("dnsjava", "dnsjava", "3.5.2")
implementation("net.sourceforge.argparse4j", "argparse4j", "0.9.0")
implementation("org.jline", "jline", "3.23.0")
implementation("org.jline", "jline", "3.24.1")
implementation("org.l33tlabs.twl", "pngdecoder", "1.0")
implementation("com.github.oshi", "oshi-core", "6.4.6")
implementation("com.github.luben", "zstd-jni", "1.5.5-6", classifier = zstdNatives)
implementation("com.github.oshi", "oshi-core", "6.4.7")
implementation("com.github.luben", "zstd-jni", "1.5.5-10", classifier = zstdNatives)
implementation("org.apache.commons", "commons-lang3", "3.13.0")
implementation("org.kamranzafar", "jtar", "2.3")
implementation("org.reflections", "reflections", "0.10.2")
Expand Down Expand Up @@ -378,7 +380,7 @@ dependencies {
lwjgl("stb")

// kotlin
implementation(kotlin("reflect", "1.9.10"))
implementation(kotlin("reflect", "1.9.20"))


// platform specific
Expand Down Expand Up @@ -500,8 +502,15 @@ val fatJar = task("fatJar", type = Jar::class) {
attributes["Main-Class"] = application.mainClass
}
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
exclude("META-INF/maven/**")
from(configurations.runtimeClasspath.get().map { if (it.isDirectory) it else zipTree(it) })
with(tasks["jar"] as CopySpec)

// TODO: exclude a lot of unneeded files

// remote other platforms from com.sun.jna
// remove most of it.unimi.fastutil classes
// remove META-INF/maven
}


Expand Down
Binary file added doc/img/hypixel_lobby.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
81 changes: 81 additions & 0 deletions doc/rendering/Entities.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Entity rendering

Entity rendering is a quite hard topic.

## Classes

- Player renderer
- player model
- feature renderer:
- armor
- stuck arrows

## Skeletal models

- SkeletalModel (raw data)
- BakedSkeletalModel (baked mesh)
- SkeletalInstance (renders baked mesh, contains transform values)
- Wrapper (indirectly accesses skeletal instance transforms)

## Model designing

### Entities

Entities are always designed without any rotation (i.e. `yaw`=`0`)

## Things to consider

- name rendering (without culling and visible through walls) (and scoreboard objective)
- hitbox rendering
- entity model itself
- player with arms, legs, ...
- has animations, ...
- different poses (sneaking, etc)
- yaw vs head yaw
- "features"
- armor (and armor trims)
- elytra
- stuck arrows (and bee stingers)
- cape
- shoulder entities (parrots)
- held item
- light (shade and lightmap)

## General

- render layers (opaque -> transparent -> translucent -> ...) (but face culling enabled)
- sort in layers after distance (or -distance)
- there are also invisible renderers (like AreaEffectCloud is just emitting particles)
- update all models async (with their visibility, etc)
- queue for unloading and loading meshes before draw (better while async preparing to save time. Maybe port that system to block entities)
- Loop over all visible entity renderers and work on the entity layer as needed
- update visible and not visible
- entity name is also visible through walls, rest not
- also with frustum (no need to update renderers that are out of the frustum)
- -> handle occlusion differently from visibility
- option to turn on/off "features"
- how to register entity models?
- loop over all entity (and block entity) types and register?
- store entities in octree like structure
- ways faster collisions with them -> physics
- way faster getInRadius -> particles, maybe entities in the future

## Hitboxes

- Create line mesh with default aabb
- interpolate
- aabb (not at all, taken from renderInfo)
- color: 0.5s
- velocity (ticks)
- direction (taken from renderInfo)
- eye height (taken from renderInfo)
- they must alight with the matrix handler -> mustn't be a frame too late/early
- Store offset and rotation as uniform
- Make hitbox a default feature of entity renderer
- highest priority (enables cheap gpu clipping)
- dynamic enabling and disabling (hitbox manager, keybinding)

## Tests

- hitbox (data, loading, unloading)
- collect visible meshes (with sorting, priority type and distance)
6 changes: 3 additions & 3 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ kotlin.code.style=official
javafx.version=19.0.2.1
lwjgl.version=3.3.3
ikonli.version=12.3.1
netty.version=4.1.99.Final
jackson.version=2.15.2
kutil.version=1.23.2
netty.version=4.1.100.Final
jackson.version=2.15.3
kutil.version=1.24.3
glm.version=0.9.9.1-12
3 changes: 1 addition & 2 deletions schemas/assets_properties.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@
"type": "object",
"properties": {
"index_version": {
"type": "string",
"pattern": "\\d+\\.\\d{1,2}"
"type": "string"
},
"index_hash": {
"$ref": "#/definitions/hash"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.kutil.concurrent.lock.thread.ThreadLock
import de.bixilon.kutil.observer.DataObserver
import de.bixilon.kutil.reflection.ReflectionUtil.forceSet
import de.bixilon.kutil.reflection.ReflectionUtil.jvmField
import de.bixilon.minosoft.data.registries.blocks.light.LightProperties
import de.bixilon.minosoft.data.registries.blocks.light.OpaqueProperty
import de.bixilon.minosoft.data.registries.blocks.settings.BlockSettings
Expand All @@ -40,7 +41,6 @@ import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
import de.bixilon.minosoft.protocol.versions.Versions
import org.objenesis.ObjenesisStd
import org.testng.annotations.Test
import kotlin.reflect.jvm.javaField

const val SECTIONS = 16

Expand All @@ -50,7 +50,7 @@ object LightTestingUtil {
fun createConnection(): PlayConnection {
val connection = ObjenesisStd().newInstance(PlayConnection::class.java)

Connection::events.javaField!!.forceSet(connection, EventMaster())
Connection::events.jvmField.forceSet(connection, EventMaster())
return connection
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ internal object MinosoftSIT {
@BeforeSuite
fun setup() {
Log.ASYNC_LOGGING = false
Log.log(LogMessageType.OTHER, LogLevels.INFO) { "This is java version ${System.getProperty("java.version")}" }
RunConfiguration.VERBOSE_LOGGING = true
KUtil.initBootClasses()
KUtil.initPlayClasses()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
* Minosoft
* Copyright (C) 2020-2023 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/

package de.bixilon.minosoft.commands.nodes

import de.bixilon.kutil.reflection.ReflectionUtil.forceSet
import de.bixilon.minosoft.commands.stack.CommandStack
import de.bixilon.minosoft.commands.suggestion.Suggestion
import de.bixilon.minosoft.commands.util.CommandReader
import de.bixilon.minosoft.data.chat.signature.signer.DummyMessageSigner
import de.bixilon.minosoft.modding.event.master.EventMaster
import de.bixilon.minosoft.protocol.network.connection.play.PacketTestUtil.assertPacket
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
import de.bixilon.minosoft.protocol.network.connection.play.util.ConnectionUtil
import de.bixilon.minosoft.protocol.network.network.client.test.TestNetwork
import de.bixilon.minosoft.protocol.packets.c2s.play.chat.ChatMessageC2SP
import de.bixilon.minosoft.protocol.versions.Versions
import de.bixilon.minosoft.terminal.cli.CLI
import de.bixilon.minosoft.test.ITUtil.allocate
import org.testng.Assert.assertEquals
import org.testng.Assert.assertTrue
import org.testng.annotations.Test
import java.security.SecureRandom

@Test(groups = ["brigadier"])
class ChatNodeTest {
private var cmd = 0
private var cmi = 0
private val root = ConnectionNode().apply { addChild(LiteralNode("cmd", executor = { cmd++ })) }

init {
CLI.commands.addChild(LiteralNode("cmi", executor = { cmi++ }))
}

private fun create(cli: Boolean): ChatNode {
return ChatNode("", allowCLI = cli)
}

private fun ChatNode.execute(command: String): CommandStack {
val connection = PlayConnection::class.java.allocate()
connection::version.forceSet(Versions.AUTOMATIC)
connection::events.forceSet(EventMaster())
val util = ConnectionUtil::class.java.allocate()
util::signer.forceSet(DummyMessageSigner)
util::class.java.getDeclaredField("connection").apply { isAccessible = true }.forceSet(util, connection)
util::class.java.getDeclaredField("random").apply { isAccessible = true }.forceSet(util, SecureRandom())
connection::util.forceSet(util)
connection::network.forceSet(TestNetwork())
connection.commands = root
val stack = CommandStack(connection)
execute(CommandReader(command), stack)
return stack
}

private fun ChatNode.suggest(command: String): Collection<Suggestion> {
val connection = PlayConnection::class.java.allocate()
connection.commands = root
val stack = CommandStack(connection)
return getSuggestions(CommandReader(command), stack)
}

fun `normal chat sending`() {
val node = create(true)
val stack = node.execute("chat message")
val packet: ChatMessageC2SP = stack.connection.assertPacket(ChatMessageC2SP::class.java)
assertEquals(packet.message, "chat message")
}

fun `command chat sending`() {
val node = create(true)
val previous = cmd
val stack = node.execute("/cmd")
assertTrue(cmd == previous + 1)
val packet: ChatMessageC2SP = stack.connection.assertPacket(ChatMessageC2SP::class.java) // old version
assertEquals(packet.message, "/cmd")
}

fun `internal execution`() {
val node = create(true)
val previous = cmi
val stack = node.execute(".cmi")
assertTrue(cmi == previous + 1)
}

fun `chat suggestions`() {
val node = create(true)
val suggestions = node.suggest("message")
assertEquals(suggestions.size, 0)
}

fun `command suggestions`() {
val node = create(true)
val suggestions = node.suggest("/cm")
assertEquals(suggestions.toSet(), setOf(
Suggestion(1, "cmd"),
))
}

fun `internal suggestions`() {
val node = create(true)
val suggestions = node.suggest(".cm")
assertEquals(suggestions.toSet(), setOf(
Suggestion(1, "cmi"),
))
}

fun `prefix suggestions`() {
val node = create(true)
val suggestions = node.suggest("")
assertEquals(suggestions.toSet(), setOf(
Suggestion(0, "."),
Suggestion(0, "/"),
))
}
}
Loading

0 comments on commit b27c9a5

Please sign in to comment.