From 522916ecaa26922034734256c4bcdd83b989a887 Mon Sep 17 00:00:00 2001 From: Holly Date: Sun, 5 Nov 2023 00:55:23 +0000 Subject: [PATCH] Implement fall damage & fix damage in general --- server/MPClient.ts | 15 ++++++++++++++- server/blocks/Block.ts | 6 +++++- server/blocks/BlockBehaviourStone.ts | 8 ++++++++ server/entities/Entity.ts | 27 +++++++++++++++++++++++++-- server/entities/EntityLiving.ts | 21 +++++++++++++++++---- server/entities/Player.ts | 13 ++++++------- server/enums/EntityStatus.ts | 8 ++++++++ server/enums/MaxUses.ts | 7 +++++++ server/inventories/ItemStack.ts | 28 +++++++++++++++++++++++++--- server/items/IItemBehaviour.ts | 3 +++ server/items/Item.ts | 22 ++++++++++++++++++---- server/items/ItemBehaviour.ts | 7 +++++++ server/items/ItemBehaviourTool.ts | 5 +++++ 13 files changed, 148 insertions(+), 22 deletions(-) create mode 100644 server/blocks/BlockBehaviourStone.ts create mode 100644 server/enums/EntityStatus.ts create mode 100644 server/enums/MaxUses.ts create mode 100644 server/items/ItemBehaviour.ts create mode 100644 server/items/ItemBehaviourTool.ts diff --git a/server/MPClient.ts b/server/MPClient.ts index 8f84de1..d8981ce 100644 --- a/server/MPClient.ts +++ b/server/MPClient.ts @@ -28,6 +28,7 @@ export class MPClient { private readonly socket:Socket; public entity:Player; private inventory:Inventory; + private dimension:number; private holdingIndex:number = 36; // First hotbar slot. private diggingAt:Vec3; @@ -37,6 +38,7 @@ export class MPClient { this.socket = socket; this.entity = entity; this.inventory = entity.inventory; + this.dimension = 0; this.diggingAt = new Vec3(); } @@ -69,6 +71,7 @@ export class MPClient { switch (packetId) { case Packet.Chat: this.handleChat(new PacketChat().readData(reader)); break; + case Packet.Respawn: this.handlePacketRespawn(new PacketRespawn().readData(reader)); break; case Packet.Player: this.handlePacketPlayer(new PacketPlayer().readData(reader)); break; case Packet.PlayerPosition: this.handlePacketPlayerPosition(new PacketPlayerPosition().readData(reader)); break; case Packet.PlayerLook: this.handlePacketPlayerLook(new PacketPlayerLook().readData(reader)); break; @@ -122,24 +125,35 @@ export class MPClient { this.mcServer.sendToAllClients(packet.writeData()); } + private handlePacketRespawn(packet:PacketRespawn) { + if (this.entity.health > 0) { + return; + } + } + private handlePacketPlayer(packet:PacketPlayer) { this.entity.onGround = packet.onGround; } private handlePacketPlayerPosition(packet:PacketPlayerPosition) { + this.entity.onGround = packet.onGround; this.entity.position.set(packet.x, packet.y, packet.z); } private handlePacketPlayerLook(packet:PacketPlayerLook) { + this.entity.onGround = packet.onGround; this.entity.rotation.set(packet.yaw, packet.pitch); } private handlePacketPlayerPositionLook(packet:PacketPlayerPositionLook) { + this.entity.onGround = packet.onGround; this.entity.position.set(packet.x, packet.y, packet.z); this.entity.rotation.set(packet.yaw, packet.pitch); } private handlePacketPlayerDigging(packet:PacketPlayerDigging) { + console.log(packet.status); + // Special drop item case if (packet.status === 4) { // TODO: Handle dropping items @@ -156,7 +170,6 @@ export class MPClient { if ((brokenBlockId = this.entity.world.getBlockId(this.diggingAt.x, this.diggingAt.y, this.diggingAt.z)) != 0) { const metadata = this.entity.world.getBlockMetadata(this.diggingAt.x, this.diggingAt.y, this.diggingAt.z); this.entity.world.setBlockWithNotify(this.diggingAt.x, this.diggingAt.y, this.diggingAt.z, 0); - console.log("Metadata: ", metadata); this.inventory.addItemStack(new ItemStack(Block.blockBehaviours[brokenBlockId].droppedItem(brokenBlockId), 1, metadata)); this.send(new PacketWindowItems(0, this.inventory.getInventorySize(), this.inventory.constructInventoryPayload()).writeData()); } diff --git a/server/blocks/Block.ts b/server/blocks/Block.ts index c1cfd56..ff67f02 100644 --- a/server/blocks/Block.ts +++ b/server/blocks/Block.ts @@ -2,12 +2,15 @@ import { World } from "../World"; import { BlockBehaviour } from "./BlockBehaviour"; import { BlockBehaviourFlower } from "./BlockBehaviourFlower"; import { BlockBehaviourGrass } from "./BlockBehaviourGrass"; +import { BlockBehaviourStone } from "./BlockBehaviourStone"; import { IBlockBehaviour } from "./IBlockBehaviour"; abstract class Behaviour { public static base = new BlockBehaviour(); + public static stone = new BlockBehaviourStone(); public static grass = new BlockBehaviourGrass(); + public static flower = new BlockBehaviourFlower(); } @@ -75,9 +78,10 @@ export class Block { } // Define statics here - static readonly stone = new Block(1).setBlockName("Stone"); + static readonly stone = new Block(1).setBehaviour(Behaviour.stone).setBlockName("Stone"); static readonly grass = new Block(2).setBehaviour(Behaviour.grass).setBlockName("Grass"); static readonly dirt = new Block(3).setBlockName("Dirt"); + static readonly cobblestone = new Block(4).setBlockName("Cobblestone"); static readonly bedrock = new Block(7).setBlockName("Bedrock"); diff --git a/server/blocks/BlockBehaviourStone.ts b/server/blocks/BlockBehaviourStone.ts new file mode 100644 index 0000000..fd1e789 --- /dev/null +++ b/server/blocks/BlockBehaviourStone.ts @@ -0,0 +1,8 @@ +import { Block } from "./Block"; +import { BlockBehaviour } from "./BlockBehaviour"; + +export class BlockBehaviourStone extends BlockBehaviour { + public droppedItem(blockId:number) { + return Block.cobblestone.blockId; + } +} \ No newline at end of file diff --git a/server/entities/Entity.ts b/server/entities/Entity.ts index 3457e27..1c5d870 100644 --- a/server/entities/Entity.ts +++ b/server/entities/Entity.ts @@ -22,6 +22,7 @@ export class Entity implements IEntity { public lastPosition:Vec3; public absPosition:Vec3; public lastAbsPosition:Vec3; + public motion:Vec3; public rotation:Rotation; public lastRotation:Rotation; @@ -35,6 +36,9 @@ export class Entity implements IEntity { public isDead:boolean; public fire:number; + public fallDistance:number; + + public onGround:boolean; public chunk:Chunk; @@ -47,7 +51,8 @@ export class Entity implements IEntity { public constructor(world:World) { this.entityId = Entity.nextEntityId++; - this.fire = 0; + this.fire = this.fallDistance = 0; + this.onGround = false; this.world = world; @@ -55,6 +60,7 @@ export class Entity implements IEntity { this.lastPosition = new Vec3(); this.absPosition = new Vec3(); this.lastAbsPosition = new Vec3(); + this.motion = new Vec3(); this.rotation = new Rotation(); this.lastRotation = new Rotation(); @@ -77,7 +83,7 @@ export class Entity implements IEntity { } sendToAllNearby(buffer:Buffer) { - this.world.sendToNearbyClients(this, buffer); + this.world.sendToNearbyAllNearbyClients(this, buffer); } updateMetadata() { @@ -169,9 +175,26 @@ export class Entity implements IEntity { } } + fall(distance:number) { + // TODO: Entity falling mount transfer + } + + updateFalling(distance:number) { + if (this.onGround) { + if (this.fallDistance > 0) + { + this.fall(this.fallDistance); + this.fallDistance = 0; + } + } else if (distance < 0) { + this.fallDistance -= distance; + } + } + onTick() { this.updateMetadata(); this.updateEntityChunk(); + this.updateFalling(this.motion.y); if (this.fire > 0) { if (this.fire % 20 === 0) { diff --git a/server/entities/EntityLiving.ts b/server/entities/EntityLiving.ts index f4282a1..4fb163b 100644 --- a/server/entities/EntityLiving.ts +++ b/server/entities/EntityLiving.ts @@ -2,6 +2,7 @@ import { Rotation } from "../Rotation"; import { Vec3 } from "../Vec3"; import { World } from "../World"; import { Block } from "../blocks/Block"; +import { EntityStatus } from "../enums/EntityStatus"; import { PacketAnimation } from "../packets/Animation"; import { PacketEntityLook } from "../packets/EntityLook"; import { PacketEntityLookRelativeMove } from "../packets/EntityLookRelativeMove"; @@ -12,17 +13,18 @@ import { Entity } from "./Entity"; import { IEntity } from "./IEntity"; export class EntityLiving extends Entity { - public onGround:boolean; public fallDistance:number; public timeInWater:number; public headHeight:number; + public lastHealth:number; public constructor(world:World) { super(world); this.fallDistance = this.timeInWater = 0; - this.onGround = true; this.headHeight = 1.62; + + this.lastHealth = this.health; } damageFrom(damage:number, entity?:IEntity) { @@ -31,14 +33,25 @@ export class EntityLiving extends Entity { } super.damageFrom(damage, entity); - // Send Damage Animation packet - this.sendToAllNearby(new PacketEntityStatus(this.entityId, 2).writeData()); + // Send Damage Animation packet or death packet + if (this.health === 0) { + this.sendToAllNearby(new PacketEntityStatus(this.entityId, EntityStatus.Dead).writeData()); + } else { + this.sendToAllNearby(new PacketEntityStatus(this.entityId, EntityStatus.Hurt).writeData()); + } } isInWater() { return this.world.getChunkBlockId(this.chunk, this.position.x, this.position.y + this.headHeight, this.position.z) === Block.waterStill.blockId; } + fall(distance:number) { + const adjustedFallDistance = Math.ceil(distance - 3); + if (adjustedFallDistance > 0) { + this.damageFrom(adjustedFallDistance); + } + } + onTick() { super.onTick(); diff --git a/server/entities/Player.ts b/server/entities/Player.ts index a122dcc..af86293 100644 --- a/server/entities/Player.ts +++ b/server/entities/Player.ts @@ -12,7 +12,7 @@ import { Block } from "../blocks/Block"; import PlayerInventory from "../inventories/PlayerInventory"; import { Item } from "../items/Item"; -const CHUNK_LOAD_RANGE = 5; +const CHUNK_LOAD_RANGE = 15; export class Player extends EntityLiving { public username:string; @@ -23,8 +23,6 @@ export class Player extends EntityLiving { public mpClient?:MPClient; public inventory:PlayerInventory; - private lastHealth:number; - public constructor(server:MinecraftServer, world:World, username:string) { super(world); this.server = server; @@ -42,8 +40,6 @@ export class Player extends EntityLiving { this.username = username; this.position.set(8, 64, 8); - - this.lastHealth = this.health; } public forceUpdatePlayerChunks() { @@ -97,11 +93,14 @@ export class Player extends EntityLiving { public onTick() { this.updatePlayerChunks(); + // Calculate player motion + this.motion.set(this.position.x - this.lastPosition.x, this.position.y - this.lastPosition.y, this.position.z - this.lastPosition.z); + + super.onTick(); + if (this.health != this.lastHealth) { this.lastHealth = this.health; this.mpClient?.send(new PacketUpdateHealth(this.health).writeData()); } - - super.onTick(); } } \ No newline at end of file diff --git a/server/enums/EntityStatus.ts b/server/enums/EntityStatus.ts new file mode 100644 index 0000000..17453e6 --- /dev/null +++ b/server/enums/EntityStatus.ts @@ -0,0 +1,8 @@ +export enum EntityStatus { + Unknown0, + Unknown1, + Hurt, + Dead, + Unknown4, + Unknown5 +} \ No newline at end of file diff --git a/server/enums/MaxUses.ts b/server/enums/MaxUses.ts new file mode 100644 index 0000000..c226e8c --- /dev/null +++ b/server/enums/MaxUses.ts @@ -0,0 +1,7 @@ +export enum MaxUses { + GOLD = 32, + WOOD = 59, + STONE = 131, + IRON = 250, + DIAMOND = 1561 +} \ No newline at end of file diff --git a/server/inventories/ItemStack.ts b/server/inventories/ItemStack.ts index 7941376..38b4be3 100644 --- a/server/inventories/ItemStack.ts +++ b/server/inventories/ItemStack.ts @@ -1,13 +1,18 @@ import { Block } from "../blocks/Block"; +import { IEntity } from "../entities/IEntity"; +import { Player } from "../entities/Player"; import { Item } from "../items/Item"; export class ItemStack { public readonly itemID:number; public readonly isBlock:boolean; - public readonly maxSize:number; public size:number; public damage:number; + private readonly maxSize:number; + private readonly maxDamage:number; + private readonly canBeDamaged:boolean; + public constructor(blockOrItemOrItemID:Block|Item|number, size?:number, damage?:number) { if (blockOrItemOrItemID instanceof Block && size === undefined && damage === undefined) { this.itemID = blockOrItemOrItemID.blockId; @@ -50,7 +55,9 @@ export class ItemStack { } this.isBlock = this.itemID < 256; - this.maxSize = !this.isBlock ? Item.getByShiftedItemId(this.itemID).maxStackSize : 64; + this.maxSize = this.isBlock ? 64 : Item.getByShiftedItemId(this.itemID).maxStackSize; + this.maxDamage = this.isBlock ? 0 : Item.getByShiftedItemId(this.itemID).maxDamage; + this.canBeDamaged = this.maxDamage > 0; } public insert(itemStack:ItemStack) { @@ -69,7 +76,22 @@ export class ItemStack { this.size += remainingSpace; itemStack.size -= remainingSpace; } - } + } + + public damageItem(damageAmount:number, entity:IEntity) { + if (!this.canBeDamaged) { + return; + } + + this.damage += damageAmount; + if (this.damage > this.maxDamage) { + this.size--; + if (this.size < 0) { + this.size = 0; + } + this.damage = 0; + } + } public get spaceAvaliable() { // Stack size check for Item(s) and Block(s). diff --git a/server/items/IItemBehaviour.ts b/server/items/IItemBehaviour.ts index db70273..e36f8e3 100644 --- a/server/items/IItemBehaviour.ts +++ b/server/items/IItemBehaviour.ts @@ -1,3 +1,6 @@ +import { EntityLiving } from "../entities/EntityLiving"; +import { ItemStack } from "../inventories/ItemStack"; + export interface IItemBehaviour { } \ No newline at end of file diff --git a/server/items/Item.ts b/server/items/Item.ts index 9b18e1a..241ea24 100644 --- a/server/items/Item.ts +++ b/server/items/Item.ts @@ -1,12 +1,16 @@ +import { MaxUses } from "../enums/MaxUses"; + export class Item { public static items:Array = new Array(); public maxStackSize:number; + public maxDamage:number; public shiftedItemID:number; public name:string; public constructor(itemID:number) { this.shiftedItemID = 256 + itemID; + this.maxDamage = 0; this.maxStackSize = 64; this.name = "UNNAMED"; @@ -22,6 +26,16 @@ export class Item { return this; } + public getMaxDamage() { + return this.maxDamage; + } + + public setMaxDamage(value:number) { + this.maxDamage = value; + + return this; + } + public setName(name:string) { this.name = name; @@ -33,8 +47,8 @@ export class Item { } // Define statics here - static ironShovel = new Item(0).setName("Iron Shovel"); - static ironPickaxe = new Item(1).setName("Iron Pickaxe"); - static ironAxe = new Item(2).setName("Iron Axe"); - static ironSword = new Item(11).setName("Iron Sword"); + static ironShovel = new Item(0).setMaxDamage(MaxUses.IRON).setName("Iron Shovel"); + static ironPickaxe = new Item(1).setMaxDamage(MaxUses.IRON).setName("Iron Pickaxe"); + static ironAxe = new Item(2).setMaxDamage(MaxUses.IRON).setName("Iron Axe"); + static ironSword = new Item(11).setMaxDamage(MaxUses.IRON).setName("Iron Sword"); } \ No newline at end of file diff --git a/server/items/ItemBehaviour.ts b/server/items/ItemBehaviour.ts new file mode 100644 index 0000000..3d1d0a7 --- /dev/null +++ b/server/items/ItemBehaviour.ts @@ -0,0 +1,7 @@ +import { EntityLiving } from "../entities/EntityLiving"; +import { ItemStack } from "../inventories/ItemStack"; +import { IItemBehaviour } from "./IItemBehaviour"; + +export default class ItemBehaviour implements IItemBehaviour { + +} \ No newline at end of file diff --git a/server/items/ItemBehaviourTool.ts b/server/items/ItemBehaviourTool.ts new file mode 100644 index 0000000..5346a37 --- /dev/null +++ b/server/items/ItemBehaviourTool.ts @@ -0,0 +1,5 @@ +import ItemBehaviour from "./ItemBehaviour"; + +export default class ItemBehaviourTool extends ItemBehaviour { + +} \ No newline at end of file