Skip to content

Commit

Permalink
Parse whole EntityMetadataPacket and use metadata to avoid applying v…
Browse files Browse the repository at this point in the history
…elocity to NoAI entities
  • Loading branch information
stackotter committed Jun 7, 2024
1 parent ae0bfe5 commit 7a95f80
Show file tree
Hide file tree
Showing 19 changed files with 421 additions and 97 deletions.
3 changes: 2 additions & 1 deletion Sources/Core/Sources/ECS/Components/EntityAttributes.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import FirebladeECS

/// A component storing an entity's attributes.
/// A component storing an entity's attributes. See ``EntityMetadata`` for a
/// discussion on the difference between metadata and attributes.
public class EntityAttributes: Component {
/// The attributes as key-value pairs.
private var attributes: [EntityAttributeKey: EntityAttributeValue] = [:]
Expand Down
12 changes: 12 additions & 0 deletions Sources/Core/Sources/ECS/Components/EntityMetadata.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import FirebladeECS

/// The distinction between entity metadata and entity attributes is that entity
/// attributes are for properties that can have modifiers applied (e.g. speed,
/// max health, etc).
public class EntityMetadata: Component {
/// If an entity doesn't have AI, we should ignore its velocity. For some reason the
/// server still sends us the velocity even when the entity isn't moving.
public var noAI = false

public init() {}
}
7 changes: 5 additions & 2 deletions Sources/Core/Sources/ECS/Systems/EntityMovementSystem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ public struct EntityMovementSystem: System {
EntityVelocity.self,
EntityRotation.self,
EntityLerpState.self,
EntityMetadata.self,
EntityKindId.self,
EntityOnGround.self,
excludesAll: ClientPlayerEntity.self
)

for (position, velocity, rotation, lerpState, kind, onGround) in physicsEntities {
for (position, velocity, rotation, lerpState, metadata, kind, onGround) in physicsEntities {
guard let kind = RegistryStore.shared.entityRegistry.entity(withId: kind.id) else {
log.warning("Unknown entity kind '\(kind.id)'")
continue
Expand Down Expand Up @@ -53,7 +54,9 @@ public struct EntityMovementSystem: System {
velocity.vector.z = 0
}

position.move(by: velocity.vector)
if !metadata.noAI {
position.move(by: velocity.vector)
}
}
}
}
8 changes: 6 additions & 2 deletions Sources/Core/Sources/Game.swift
Original file line number Diff line number Diff line change
Expand Up @@ -291,12 +291,16 @@ public final class Game: @unchecked Sendable {
/// - Parameters:
/// - id: The id of the entity to access.
/// - action: The action to perform on the entity if it exists.
public func accessEntity(id: Int, acquireLock: Bool = true, action: (Entity) -> Void) {
public func accessEntity(
id: Int,
acquireLock: Bool = true,
action: (Entity) throws -> Void
) rethrows {
if acquireLock { nexusLock.acquireWriteLock() }
defer { if acquireLock { nexusLock.unlock() } }

if let identifier = entityIdToEntityIdentifier[id] {
action(nexus.entity(from: identifier))
try action(nexus.entity(from: identifier))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,66 +14,90 @@ public enum ClientboundPacketError: LocalizedError {
case invalidBossBarStyleId(Int)
case duplicateBossBar(UUID)
case noSuchBossBar(UUID)
case invalidPoseId(Int)
case invalidEntityMetadataDatatypeId(Int)
case incorrectEntityMetadataDatatype(
property: String,
expectedType: String,
value: EntityMetadataPacket.Value
)

public var errorDescription: String? {
switch self {
case .invalidDifficulty:
return "Invalid difficulty."
case let .invalidGamemode(rawValue):
return """
Invalid gamemode.
Raw value: \(rawValue)
"""
Invalid gamemode.
Raw value: \(rawValue)
"""
case .invalidServerId:
return "Invalid server Id."
case .invalidJSONString:
return "Invalid JSON string."
case let .invalidInventorySlotCount(slotCount):
return """
Invalid inventory slot count.
Slot count: \(slotCount)
"""
Invalid inventory slot count.
Slot count: \(slotCount)
"""
case let .invalidInventorySlotIndex(slotIndex, windowId):
return """
Invalid inventory slot index.
Slot index: \(slotIndex)
Window Id: \(windowId)
"""
Invalid inventory slot index.
Slot index: \(slotIndex)
Window Id: \(windowId)
"""
case let .invalidChangeGameStateReasonRawValue(rawValue):
return """
Invalid change game state reason.
Raw value: \(rawValue)
"""
Invalid change game state reason.
Raw value: \(rawValue)
"""
case let .invalidDimension(identifier):
return """
Invalid dimension.
Identifier: \(identifier)
"""
Invalid dimension.
Identifier: \(identifier)
"""
case let .invalidBossBarActionId(actionId):
return """
Invalid boss bar action id.
Id: \(actionId)
"""
Invalid boss bar action id.
Id: \(actionId)
"""
case let .invalidBossBarColorId(colorId):
return """
Invalid boss bar color id.
Id: \(colorId)
"""
Invalid boss bar color id.
Id: \(colorId)
"""
case let .invalidBossBarStyleId(styleId):
return """
Invalid boss bar style id.
Id: \(styleId)
"""
Invalid boss bar style id.
Id: \(styleId)
"""
case let .duplicateBossBar(uuid):
return """
Received duplicate boss bar.
UUID: \(uuid.uuidString)
"""
Received duplicate boss bar.
UUID: \(uuid.uuidString)
"""
case let .noSuchBossBar(uuid):
return """
Received update for non-existent boss bar.
UUID: \(uuid)
"""
Received update for non-existent boss bar.
UUID: \(uuid)
"""
case let .invalidPoseId(poseId):
return """
Received invalid pose id.
Id: \(poseId)
"""
case let .invalidEntityMetadataDatatypeId(datatypeId):
return """
Received invalid entity metadata datatype id.
Id: \(datatypeId)
"""
case let .incorrectEntityMetadataDatatype(property, expectedType, value):
return """
Received entity metadata property with invalid data type.
Property name: \(property)
Expected type: \(expectedType)
Value: \(value)
"""
}
}
}
Expand Down
33 changes: 27 additions & 6 deletions Sources/Core/Sources/Network/Protocol/Packets/PacketReader.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Foundation
import FirebladeMath
import Foundation

/// A wrapper around ``Buffer`` that is specialized for reading Minecraft packets.
public struct PacketReader {
Expand Down Expand Up @@ -46,6 +46,25 @@ public struct PacketReader {
return bool
}

/// Optionally reads a value (assuming that the value's presence is indicated by a boolean
/// field directly preceding it).
public mutating func readOptional<T>(_ inner: (inout Self) throws -> T) throws -> T? {
if try readBool() {
return try inner(&self)
} else {
return nil
}
}

/// Reads a direction (represented as a VarInt).
public mutating func readDirection() throws -> Direction {
let rawValue = try readVarInt()
guard let direction = Direction(rawValue: rawValue) else {
throw PacketReaderError.invalidDirection(rawValue)
}
return direction
}

/// Reads a signed byte.
/// - Returns: A signed byte.
/// - Throws: A ``BufferError`` if out of bounds.
Expand Down Expand Up @@ -224,9 +243,9 @@ public struct PacketReader {
let val = try buffer.readLong(endianness: .big)

// Extract the bit patterns (in the order x, then z, then y)
var x = UInt32(val >> 38) // x is 26 bit
var z = UInt32((val << 26) >> 38) // z is 26 bit
var y = UInt32(val & 0xfff) // y is 12 bit
var x = UInt32(val >> 38) // x is 26 bit
var z = UInt32((val << 26) >> 38) // z is 26 bit
var y = UInt32(val & 0xfff) // y is 12 bit

// x and z are 26-bit signed integers, y is a 12-bit signed integer
let xSignBit = (x & (1 << 25)) >> 25
Expand All @@ -238,7 +257,7 @@ public struct PacketReader {
x |= 0b111111 << 26
}
if ySignBit == 1 {
y |= 0b11111111111111111111 << 12
y |= 0b1111_11111111_11111111 << 12
}
if zSignBit == 1 {
z |= 0b111111 << 26
Expand All @@ -257,7 +276,9 @@ public struct PacketReader {
/// - Parameter pitchFirst: If `true`, pitch is read before yaw.
/// - Returns: An entity rotation in radians.
/// - Throws: A ``BufferError`` if any reads go out of bounds.
public mutating func readEntityRotation(pitchFirst: Bool = false) throws -> (pitch: Float, yaw: Float) {
public mutating func readEntityRotation(pitchFirst: Bool = false) throws -> (
pitch: Float, yaw: Float
) {
var pitch: Float = 0
if pitchFirst {
pitch = try readAngle()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ public enum PacketReaderError: Error {
case stringTooLong(length: Int)
case invalidNBT(Error)
case invalidIdentifier(String)
case invalidDirection(Int)
}
Loading

0 comments on commit 7a95f80

Please sign in to comment.