Block getting
This commit is contained in:
parent
d8d1eabcf4
commit
f1c4dcda75
20 changed files with 406 additions and 162 deletions
|
@ -7,35 +7,25 @@ export class NibbleArray {
|
|||
} else if (size instanceof Uint8Array) {
|
||||
this.array = new Uint8Array(size);
|
||||
} else {
|
||||
this.array = new Uint8Array(Math.round(size / 2));
|
||||
this.array = new Uint8Array(size >> 1);
|
||||
}
|
||||
}
|
||||
|
||||
// We can determine which side of the byte to read
|
||||
// from if the halved index has a remainder.
|
||||
private isLowOrHighNibble(index:number) {
|
||||
return index % 1 !== 0;
|
||||
}
|
||||
|
||||
public get(index:number) {
|
||||
index = index / 2;
|
||||
|
||||
const arrayIndex = index | 0;
|
||||
if (this.isLowOrHighNibble(index)) {
|
||||
return this.array[arrayIndex] >> 4;
|
||||
const arrayIndex = index >> 1;
|
||||
if ((index & 1) === 0) {
|
||||
return this.array[arrayIndex] & 0xf;
|
||||
} else {
|
||||
return this.array[arrayIndex] & 0x0f;
|
||||
return this.array[arrayIndex] >> 4 & 0xf;
|
||||
}
|
||||
}
|
||||
|
||||
public set(index:number, value:number) {
|
||||
index = index / 2;
|
||||
|
||||
const arrayIndex = index | 0;
|
||||
if (this.isLowOrHighNibble(index)) {
|
||||
this.array[arrayIndex] = value << 4 | this.array[arrayIndex] & 0xf;
|
||||
const arrayIndex = index >> 1;
|
||||
if ((index & 1) === 0) {
|
||||
this.array[arrayIndex] = this.array[arrayIndex] & 0xf0 | value & 0xf;
|
||||
} else {
|
||||
this.array[arrayIndex] = this.array[arrayIndex] & 0xf0 | value;
|
||||
this.array[arrayIndex] = this.array[arrayIndex] & 0xf | (value & 0xf) << 4;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,9 @@ import { PacketPlayerBlockPlacement } from "./packets/PlayerBlockPlacement";
|
|||
import { Inventory } from "./inventories/Inventory";
|
||||
import { PacketHoldingChange } from "./packets/HoldingChange";
|
||||
import { PacketDisconnectKick } from "./packets/DisconnectKick";
|
||||
import { ItemStack } from "./inventories/ItemStack";
|
||||
import { PacketWindowItems } from "./packets/WindowItems";
|
||||
import { Block } from "./blocks/Block";
|
||||
|
||||
export class MPClient {
|
||||
private readonly mcServer:MinecraftServer;
|
||||
|
@ -86,24 +89,24 @@ export class MPClient {
|
|||
if (message[0].startsWith("/")) {
|
||||
packet.message = "";
|
||||
if (message[0] === "/tp") {
|
||||
const x = this.entity.x = parseFloat(message[1]);
|
||||
const y = this.entity.y = parseFloat(message[2]);
|
||||
const z = this.entity.z = parseFloat(message[3]);
|
||||
const x = this.entity.position.x = parseFloat(message[1]);
|
||||
const y = this.entity.position.y = parseFloat(message[2]);
|
||||
const z = this.entity.position.z = parseFloat(message[3]);
|
||||
this.send(new PacketPlayerPositionLook(x, y, y + 0.62, z, 0, 0, false).writeData());
|
||||
Console.printInfo(packet.message = `Teleported ${this.entity.username} to ${message[1]} ${message[2]} ${message[3]}`);
|
||||
} else if (message[0] === "/csay") {
|
||||
this.mcServer.sendChatMessage(`[CONSOLE] ${message.slice(1, message.length).join(" ")}`);
|
||||
} else if (message[0] === "/top") {
|
||||
packet.message = `Woosh!`;
|
||||
const topBlock = this.entity.world.getChunk(this.entity.x >> 4, this.entity.z >> 4).getTopBlockY(this.entity.x & 0xf, this.entity.z & 0xf);
|
||||
this.send(new PacketPlayerPosition(this.entity.x, topBlock + 3, topBlock + 3.62, this.entity.z, false).writeData());
|
||||
const topBlock = this.entity.chunk.getTopBlockY(this.entity.position.x & 0xf, this.entity.position.z & 0xf);
|
||||
this.send(new PacketPlayerPosition(this.entity.position.x, topBlock + 3, topBlock + 3.62, this.entity.position.z, false).writeData());
|
||||
} else if (message[0] === "/tpx") {
|
||||
const dimension = parseInt(message[1]);
|
||||
if (this.mcServer.worlds.has(dimension)) {
|
||||
packet.message = "\u00a76Switching dimensions...";
|
||||
this.switchDimension(dimension);
|
||||
} else {
|
||||
packet.message = `\u00a7cNo dimension by id "${dimension} exists!"`;
|
||||
packet.message = `\u00a7cNo dimension by id "${dimension}" exists!`;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -124,22 +127,16 @@ export class MPClient {
|
|||
}
|
||||
|
||||
private handlePacketPlayerPosition(packet:PacketPlayerPosition) {
|
||||
this.entity.x = packet.x;
|
||||
this.entity.y = packet.y;
|
||||
this.entity.z = packet.z;
|
||||
this.entity.position.set(packet.x, packet.y, packet.z);
|
||||
}
|
||||
|
||||
private handlePacketPlayerLook(packet:PacketPlayerLook) {
|
||||
this.entity.yaw = packet.yaw;
|
||||
this.entity.pitch = packet.pitch;
|
||||
this.entity.rotation.set(packet.yaw, packet.pitch);
|
||||
}
|
||||
|
||||
private handlePacketPlayerPositionLook(packet:PacketPlayerPositionLook) {
|
||||
this.entity.x = packet.x;
|
||||
this.entity.y = packet.y;
|
||||
this.entity.z = packet.z;
|
||||
this.entity.yaw = packet.yaw;
|
||||
this.entity.pitch = packet.pitch;
|
||||
this.entity.position.set(packet.x, packet.y, packet.z);
|
||||
this.entity.rotation.set(packet.yaw, packet.pitch);
|
||||
}
|
||||
|
||||
private handlePacketPlayerDigging(packet:PacketPlayerDigging) {
|
||||
|
@ -155,20 +152,37 @@ export class MPClient {
|
|||
if (packet.status === 0) {
|
||||
// Started digging
|
||||
} else if (packet.status === 2) {
|
||||
if (this.entity.world.getBlockId(this.diggingAt.x, this.diggingAt.y, this.diggingAt.z) != 0) {
|
||||
let brokenBlockId:number;
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public getHeldItemStack() {
|
||||
return this.inventory.getSlotItemStack(this.holdingIndex);
|
||||
}
|
||||
|
||||
private handlePacketBlockPlacement(packet:PacketPlayerBlockPlacement) {
|
||||
this.diggingAt.set(packet.x, packet.y, packet.z);
|
||||
this.mapCoordsFromFace(this.diggingAt, packet.face);
|
||||
|
||||
const itemStack = this.inventory.getSlotItemStack(this.holdingIndex);
|
||||
if (itemStack != null && itemStack.size > 0 && this.entity.world.getBlockId(this.diggingAt.x, this.diggingAt.y, this.diggingAt.z) === 0) {
|
||||
itemStack.size--;
|
||||
this.entity.world.setBlockAndMetadataWithNotify(this.diggingAt.x, this.diggingAt.y, this.diggingAt.z, itemStack.itemID, itemStack.damage);
|
||||
const itemStack = this.getHeldItemStack();
|
||||
if (itemStack == null || itemStack.size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (itemStack.isBlock) {
|
||||
if (this.entity.world.getBlockId(this.diggingAt.x, this.diggingAt.y, this.diggingAt.z) === 0) {
|
||||
itemStack.size--;
|
||||
this.entity.world.setBlockAndMetadataWithNotify(this.diggingAt.x, this.diggingAt.y, this.diggingAt.z, itemStack.itemID, itemStack.damage);
|
||||
}
|
||||
} else {
|
||||
// TODO: Handle item usage
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -209,9 +223,7 @@ export class MPClient {
|
|||
|
||||
this.send(new PacketRespawn(dimension).writeData());
|
||||
//this.send(new PacketSpawnPosition(8, 64, 8).writeData());
|
||||
this.entity.x = 8;
|
||||
this.entity.y = 70;
|
||||
this.entity.z = 8;
|
||||
this.entity.position.set(8, 60, 8);
|
||||
this.send(new PacketPlayerPositionLook(8, 70, 70.62, 8, 0, 0, false).writeData());
|
||||
|
||||
this.entity.forceUpdatePlayerChunks();
|
||||
|
|
|
@ -225,10 +225,10 @@ export class MinecraftServer {
|
|||
socket.write(new PacketLoginRequest(clientEntity.entityId, "", 0, dimension).writeData());
|
||||
socket.write(new PacketSpawnPosition(8, 64, 8).writeData());
|
||||
|
||||
const thisPlayerSpawn = new PacketNamedEntitySpawn(clientEntity.entityId, clientEntity.username, clientEntity.absX, clientEntity.absY, clientEntity.absZ, clientEntity.absYaw, clientEntity.absPitch, 0).writeData();
|
||||
const thisPlayerSpawn = new PacketNamedEntitySpawn(clientEntity.entityId, clientEntity.username, clientEntity.absPosition.x, clientEntity.absPosition.y, clientEntity.absPosition.z, clientEntity.absRotation.yaw, clientEntity.absRotation.pitch, clientEntity.mpClient?.getHeldItemStack()?.itemID).writeData();
|
||||
world.players.forEach(player => {
|
||||
if (player.entityId !== clientEntity.entityId && clientEntity.distanceTo(player) < World.ENTITY_MAX_SEND_DISTANCE) {
|
||||
socket.write(new PacketNamedEntitySpawn(player.entityId, player.username, player.absX, player.absY, player.absZ, player.absYaw, player.absPitch, 0).writeData());
|
||||
socket.write(new PacketNamedEntitySpawn(player.entityId, player.username, player.absPosition.x, player.absPosition.y, player.absPosition.z, player.absRotation.yaw, player.absRotation.pitch, player.mpClient?.getHeldItemStack()?.itemID).writeData());
|
||||
player.mpClient?.send(thisPlayerSpawn);
|
||||
}
|
||||
});
|
||||
|
@ -237,7 +237,6 @@ export class MinecraftServer {
|
|||
|
||||
const playerInventory = clientEntity.inventory;
|
||||
socket.write(new PacketWindowItems(0, playerInventory.getInventorySize(), playerInventory.constructInventoryPayload()).writeData());
|
||||
console.log(new PacketWindowItems(0, playerInventory.getInventorySize(), playerInventory.constructInventoryPayload()).writeData());
|
||||
} else {
|
||||
socket.write(new PacketDisconnectKick("Failed to find world to put player in.").writeData());
|
||||
}
|
||||
|
|
19
server/Rotation.ts
Normal file
19
server/Rotation.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { Vec2 } from "./Vec2";
|
||||
|
||||
export class Rotation extends Vec2 {
|
||||
public get yaw() {
|
||||
return this.x;
|
||||
}
|
||||
|
||||
public set yaw(value:number) {
|
||||
this.x = value;
|
||||
}
|
||||
|
||||
public get pitch() {
|
||||
return this.y;
|
||||
}
|
||||
|
||||
public set pitch(value:number) {
|
||||
this.y = value;
|
||||
}
|
||||
}
|
31
server/Vec2.ts
Normal file
31
server/Vec2.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
export class Vec2 {
|
||||
public x:number;
|
||||
public y:number;
|
||||
|
||||
public constructor(x?:Vec2 | number, y?:number) {
|
||||
if (typeof(x) === "number" && typeof(y) === "number") {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
} else if (typeof(x) === "number" && typeof(y) !== "number") {
|
||||
this.x = x;
|
||||
this.y = x;
|
||||
} else if (x instanceof Vec2) {
|
||||
this.x = x.x;
|
||||
this.y = x.y;
|
||||
} else {
|
||||
this.x = this.y = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public set(x?:Vec2 | number, y?:number) {
|
||||
if (x instanceof Vec2) {
|
||||
this.x = x.x;
|
||||
this.y = x.y;
|
||||
} else if (typeof(x) === "number" && typeof(y) === "number") {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
} else {
|
||||
this.x = this.y = 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@ export class Vec3 {
|
|||
public y:number;
|
||||
public z:number;
|
||||
|
||||
public constructor(x?:number, y?:number, z?:number) {
|
||||
public constructor(x?:Vec3 | number, y?:number, z?:number) {
|
||||
if (typeof(x) === "number" && typeof(y) === "number" && typeof(z) === "number") {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
|
@ -12,14 +12,26 @@ export class Vec3 {
|
|||
this.x = x;
|
||||
this.y = x;
|
||||
this.z = x;
|
||||
} else if (x instanceof Vec3) {
|
||||
this.x = x.x;
|
||||
this.y = x.y;
|
||||
this.z = x.z;
|
||||
} else {
|
||||
this.x = this.y = this.z = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public set(x:number, y:number, z:number) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
public set(x?:Vec3 | number, y?:number, z?:number) {
|
||||
if (x instanceof Vec3) {
|
||||
this.x = x.x;
|
||||
this.y = x.y;
|
||||
this.z = x.z;
|
||||
} else if (typeof(x) === "number" && typeof(y) === "number" && typeof(z) === "number") {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
} else {
|
||||
this.x = this.y = this.z = 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,13 +1,19 @@
|
|||
import { World } from "../World";
|
||||
import { BlockBehaviour } from "./BlockBehaviour";
|
||||
import { BlockBehaviourFlower } from "./BlockBehaviourFlower";
|
||||
import { BlockBehaviourGrass } from "./BlockBehaviourGrass";
|
||||
import { IBlockBehaviour } from "./IBlockBehaviour";
|
||||
|
||||
abstract class Behaviour {
|
||||
public static base = new BlockBehaviour();
|
||||
|
||||
public static grass = new BlockBehaviourGrass();
|
||||
public static flower = new BlockBehaviourFlower();
|
||||
}
|
||||
|
||||
export class Block {
|
||||
public readonly blockId:number;
|
||||
|
||||
public static readonly blocks:Array<Block> = new Array<Block>();
|
||||
public static readonly lightPassage:Array<number> = new Array<number>();
|
||||
public static readonly blockBehaviours:Array<IBlockBehaviour> = new Array<IBlockBehaviour>();
|
||||
|
@ -17,6 +23,7 @@ export class Block {
|
|||
Block.blocks[blockId] = this;
|
||||
Block.lightPassage[blockId] = 0;
|
||||
Block.blockNames[blockId] = "";
|
||||
Block.blockBehaviours[blockId] = Behaviour.base;
|
||||
this.blockId = blockId;
|
||||
}
|
||||
|
||||
|
@ -60,14 +67,16 @@ export class Block {
|
|||
}
|
||||
|
||||
public neighborBlockChange(world:World, x:number, y:number, z:number, blockId:number) {
|
||||
if (this.behaviour !== undefined) {
|
||||
this.behaviour.neighborBlockChange(world, x, y, z, blockId);
|
||||
}
|
||||
this.behaviour.neighborBlockChange(world, x, y, z, blockId);
|
||||
}
|
||||
|
||||
public droppedItem(blockId:number) {
|
||||
this.behaviour.droppedItem(blockId);
|
||||
}
|
||||
|
||||
// Define statics here
|
||||
static readonly stone = new Block(1).setBlockName("Stone");
|
||||
static readonly grass = new Block(2).setBlockName("Grass");
|
||||
static readonly grass = new Block(2).setBehaviour(Behaviour.grass).setBlockName("Grass");
|
||||
static readonly dirt = new Block(3).setBlockName("Dirt");
|
||||
|
||||
static readonly bedrock = new Block(7).setBlockName("Bedrock");
|
||||
|
|
|
@ -3,4 +3,5 @@ import { IBlockBehaviour } from "./IBlockBehaviour";
|
|||
|
||||
export class BlockBehaviour implements IBlockBehaviour {
|
||||
public neighborBlockChange(world:World, x:number, y:number, z:number, blockId:number) {}
|
||||
public droppedItem(blockId:number) { return blockId; }
|
||||
}
|
8
server/blocks/BlockBehaviourGrass.ts
Normal file
8
server/blocks/BlockBehaviourGrass.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import { Block } from "./Block";
|
||||
import { BlockBehaviour } from "./BlockBehaviour";
|
||||
|
||||
export class BlockBehaviourGrass extends BlockBehaviour {
|
||||
public droppedItem(blockId:number) {
|
||||
return Block.dirt.blockId;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
import { World } from "../World";
|
||||
|
||||
export interface IBlockBehaviour {
|
||||
neighborBlockChange(world:World, x:number, y:number, z:number, blockId:number): void
|
||||
neighborBlockChange(world:World, x:number, y:number, z:number, blockId:number): void,
|
||||
droppedItem: (blockId:number) => number
|
||||
}
|
|
@ -1,8 +1,14 @@
|
|||
import { Chunk } from "../Chunk";
|
||||
import { MetadataEntry, MetadataWriter } from "../MetadataWriter";
|
||||
import { Rotation } from "../Rotation";
|
||||
import { Vec3 } from "../Vec3";
|
||||
import { World } from "../World";
|
||||
import { MetadataFieldType } from "../enums/MetadataFieldType";
|
||||
import { PacketEntityLook } from "../packets/EntityLook";
|
||||
import { PacketEntityLookRelativeMove } from "../packets/EntityLookRelativeMove";
|
||||
import { PacketEntityMetadata } from "../packets/EntityMetadata";
|
||||
import { PacketEntityRelativeMove } from "../packets/EntityRelativeMove";
|
||||
import { PacketEntityTeleport } from "../packets/EntityTeleport";
|
||||
import { IEntity } from "./IEntity";
|
||||
|
||||
export class Entity implements IEntity {
|
||||
|
@ -11,14 +17,22 @@ export class Entity implements IEntity {
|
|||
public entityId:number;
|
||||
|
||||
public world:World;
|
||||
public x:number;
|
||||
public y:number;
|
||||
public z:number;
|
||||
public lastX:number;
|
||||
public lastY:number;
|
||||
public lastZ:number;
|
||||
|
||||
public position:Vec3;
|
||||
public lastPosition:Vec3;
|
||||
public absPosition:Vec3;
|
||||
public lastAbsPosition:Vec3;
|
||||
|
||||
public rotation:Rotation;
|
||||
public lastRotation:Rotation;
|
||||
public absRotation:Rotation;
|
||||
public lastAbsRotation:Rotation;
|
||||
|
||||
public velocity:Vec3;
|
||||
|
||||
public health:number;
|
||||
public wasHurt:boolean;
|
||||
public isDead:boolean;
|
||||
|
||||
public fire:number;
|
||||
|
||||
|
@ -36,12 +50,26 @@ export class Entity implements IEntity {
|
|||
this.fire = 0;
|
||||
|
||||
this.world = world;
|
||||
this.x = this.y = this.z = this.lastX = this.lastY = this.lastZ = 0;
|
||||
|
||||
this.position = new Vec3();
|
||||
this.lastPosition = new Vec3();
|
||||
this.absPosition = new Vec3();
|
||||
this.lastAbsPosition = new Vec3();
|
||||
|
||||
this.rotation = new Rotation();
|
||||
this.lastRotation = new Rotation();
|
||||
this.absRotation = new Rotation();
|
||||
this.lastAbsRotation = new Rotation();
|
||||
|
||||
this.velocity = new Vec3();
|
||||
|
||||
this.crouching = this.lastCrouchState = this.lastFireState = this.queuedChunkUpdate = false;
|
||||
|
||||
this.chunk = world.getChunk(this.x >> 4, this.z >> 4);
|
||||
this.chunk = world.getChunk(this.position.x >> 4, this.position.z >> 4);
|
||||
|
||||
this.health = 20;
|
||||
this.wasHurt = false;
|
||||
this.isDead = false;
|
||||
}
|
||||
|
||||
sendToNearby(buffer:Buffer) {
|
||||
|
@ -61,10 +89,7 @@ export class Entity implements IEntity {
|
|||
// 1 = On Fire
|
||||
// 2 = Player crouched
|
||||
// 4 = Player on mount?
|
||||
//metadata.addMetadataEntry(0, new MetadataEntry(MetadataFieldType.Byte, 1));
|
||||
if (crouchStateChanged) {
|
||||
metadata.addMetadataEntry(0, new MetadataEntry(MetadataFieldType.Byte, Number(this.fire > 0) + Number(this.crouching) * 2));
|
||||
}
|
||||
metadata.addMetadataEntry(0, new MetadataEntry(MetadataFieldType.Byte, Number(this.fire > 0) + Number(this.crouching) * 2));
|
||||
|
||||
this.sendToNearby(new PacketEntityMetadata(this.entityId, metadata.writeBuffer()).writeData());
|
||||
|
||||
|
@ -74,9 +99,9 @@ export class Entity implements IEntity {
|
|||
}
|
||||
|
||||
distanceTo(entity:IEntity) {
|
||||
const dX = entity.x - this.x,
|
||||
dY = entity.y - this.y,
|
||||
dZ = entity.z - this.z;
|
||||
const dX = entity.position.x - this.position.x,
|
||||
dY = entity.position.y - this.position.y,
|
||||
dZ = entity.position.z - this.position.z;
|
||||
|
||||
return Math.sqrt(Math.pow(dX, 2) + Math.pow(dY, 2) + Math.pow(dZ, 2));
|
||||
}
|
||||
|
@ -89,12 +114,14 @@ export class Entity implements IEntity {
|
|||
if (entity === undefined) {
|
||||
this.health -= damage;
|
||||
}
|
||||
|
||||
this.wasHurt = true;
|
||||
}
|
||||
|
||||
updateEntityChunk() {
|
||||
const bitX = this.x >> 4;
|
||||
const bitZ = this.z >> 4;
|
||||
if (bitX != this.lastX >> 4 || bitZ != this.lastZ >> 4 || this.queuedChunkUpdate) {
|
||||
const bitX = this.position.x >> 4;
|
||||
const bitZ = this.position.z >> 4;
|
||||
if (bitX != this.lastPosition.x >> 4 || bitZ != this.lastPosition.z >> 4 || this.queuedChunkUpdate) {
|
||||
if (this.world.chunkExists(bitX, bitZ)) {
|
||||
this.chunk = this.world.getChunk(bitX, bitZ);
|
||||
this.queuedChunkUpdate = false;
|
||||
|
@ -104,6 +131,44 @@ export class Entity implements IEntity {
|
|||
}
|
||||
}
|
||||
|
||||
private constrainRot(rot:number) {
|
||||
return Math.min(Math.max(rot, -128), 127);
|
||||
}
|
||||
|
||||
private sendPositionUpdate() {
|
||||
this.absPosition.set(Math.floor(this.position.x * 32), Math.floor(this.position.y * 32), Math.floor(this.position.z * 32));
|
||||
|
||||
// This is suuuuuper jank
|
||||
this.absRotation.set(
|
||||
this.constrainRot(Math.floor(((this.rotation.yaw - 180 >= 0 ? this.rotation.yaw - 180 : (this.rotation.yaw - 180) % 360 + 360) % 360 / 360) * 256) - 128), // Yaw
|
||||
this.constrainRot(Math.floor((this.rotation.pitch % 360 * 256) / 360)) // Pitch
|
||||
);
|
||||
const diffX = this.absPosition.x - this.lastAbsPosition.x;
|
||||
const diffY = this.absPosition.y - this.lastAbsPosition.y;
|
||||
const diffZ = this.absPosition.z - this.lastAbsPosition.z;
|
||||
const diffYaw = this.absRotation.yaw - this.lastAbsRotation.yaw;
|
||||
const diffPitch = this.absRotation.pitch - this.lastAbsRotation.pitch;
|
||||
|
||||
const doRelativeMove = Math.abs(diffX) >= 4 || Math.abs(diffY) >= 4 || Math.abs(diffZ) >= 4;
|
||||
const doLook = Math.abs(diffYaw) >= 4 || Math.abs(diffPitch) >= 4;
|
||||
if (Math.abs(diffX) > 128 || Math.abs(diffY) > 128 || Math.abs(diffZ) > 128) {
|
||||
this.world.sendToNearbyClients(this, new PacketEntityTeleport(this.entityId, this.absPosition.x, this.absPosition.y, this.absPosition.z, this.absRotation.yaw, this.absRotation.pitch).writeData());
|
||||
} else if (doRelativeMove && doLook) {
|
||||
this.world.sendToNearbyClients(this, new PacketEntityLookRelativeMove(this.entityId, diffX, diffY, diffZ, this.absRotation.yaw, this.absRotation.pitch).writeData());
|
||||
} else if (doRelativeMove) {
|
||||
this.world.sendToNearbyClients(this, new PacketEntityRelativeMove(this.entityId, diffX, diffY, diffZ).writeData());
|
||||
} else if (doLook) {
|
||||
this.world.sendToNearbyClients(this, new PacketEntityLook(this.entityId, this.absRotation.yaw, this.absRotation.pitch).writeData());
|
||||
}
|
||||
|
||||
if (doRelativeMove) {
|
||||
this.lastAbsPosition.set(this.absPosition);
|
||||
}
|
||||
if (doLook) {
|
||||
this.lastAbsRotation.set(this.absRotation);
|
||||
}
|
||||
}
|
||||
|
||||
onTick() {
|
||||
this.updateMetadata();
|
||||
this.updateEntityChunk();
|
||||
|
@ -116,8 +181,17 @@ export class Entity implements IEntity {
|
|||
this.fire--;
|
||||
}
|
||||
|
||||
this.lastX = this.x;
|
||||
this.lastY = this.y;
|
||||
this.lastZ = this.z;
|
||||
if (!this.isDead && this.health <= 0) {
|
||||
this.isDead = true;
|
||||
|
||||
}
|
||||
|
||||
if (this.wasHurt) {
|
||||
this.wasHurt = false;
|
||||
}
|
||||
|
||||
this.sendPositionUpdate();
|
||||
|
||||
this.lastPosition.set(this.position);
|
||||
}
|
||||
}
|
|
@ -6,11 +6,8 @@ export class EntityItem extends Entity {
|
|||
public age:number;
|
||||
public itemStack:ItemStack;
|
||||
|
||||
public constructor(world:World, x:number, y:number, z:number, itemStack:ItemStack) {
|
||||
public constructor(world:World, itemStack:ItemStack) {
|
||||
super(world);
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
|
||||
this.itemStack = itemStack;
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { Rotation } from "../Rotation";
|
||||
import { Vec3 } from "../Vec3";
|
||||
import { World } from "../World";
|
||||
import { Block } from "../blocks/Block";
|
||||
import { PacketAnimation } from "../packets/Animation";
|
||||
|
@ -10,30 +12,15 @@ import { Entity } from "./Entity";
|
|||
import { IEntity } from "./IEntity";
|
||||
|
||||
export class EntityLiving extends Entity {
|
||||
public yaw:number;
|
||||
public lastYaw:number;
|
||||
public pitch:number;
|
||||
public lastPitch:number;
|
||||
public onGround:boolean;
|
||||
public fallDistance:number;
|
||||
public timeInWater:number;
|
||||
public headHeight:number;
|
||||
|
||||
public absX:number;
|
||||
public absY:number;
|
||||
public absZ:number;
|
||||
public absYaw:number;
|
||||
public absPitch:number;
|
||||
public lastAbsX:number;
|
||||
public lastAbsY:number;
|
||||
public lastAbsZ:number;
|
||||
public lastAbsYaw:number;
|
||||
public lastAbsPitch:number;
|
||||
|
||||
public constructor(world:World) {
|
||||
super(world);
|
||||
|
||||
this.fallDistance = this.yaw = this.lastYaw = this.pitch = this.lastPitch = this.absX = this.absY = this.absZ = this.absYaw = this.absPitch = this.lastAbsX = this.lastAbsY = this.lastAbsZ = this.lastAbsYaw = this.lastAbsPitch = this.timeInWater = 0;
|
||||
this.fallDistance = this.timeInWater = 0;
|
||||
this.onGround = true;
|
||||
this.headHeight = 1.62;
|
||||
}
|
||||
|
@ -43,57 +30,17 @@ export class EntityLiving extends Entity {
|
|||
return;
|
||||
}
|
||||
super.damageFrom(damage, entity);
|
||||
|
||||
// Send Damage Animation packet
|
||||
this.sendToAllNearby(new PacketEntityStatus(this.entityId, 2).writeData());
|
||||
}
|
||||
|
||||
isInWater() {
|
||||
return this.world.getChunkBlockId(this.chunk, this.x, this.y + this.headHeight, this.z) === Block.waterStill.blockId;
|
||||
}
|
||||
|
||||
private constrainRot(rot:number) {
|
||||
return Math.min(Math.max(rot, -128), 127);
|
||||
}
|
||||
|
||||
private sendPositionUpdate() {
|
||||
this.absX = Math.floor(this.x * 32);
|
||||
this.absY = Math.floor(this.y * 32);
|
||||
this.absZ = Math.floor(this.z * 32);
|
||||
// This is suuuuuper jank
|
||||
this.absYaw = this.constrainRot(Math.floor(((this.yaw - 180 >= 0 ? this.yaw - 180 : (this.yaw - 180) % 360 + 360) % 360 / 360) * 256) - 128);
|
||||
this.absPitch = this.constrainRot(Math.floor((this.pitch % 360 * 256) / 360));
|
||||
const diffX = this.absX - this.lastAbsX;
|
||||
const diffY = this.absY - this.lastAbsY;
|
||||
const diffZ = this.absZ - this.lastAbsZ;
|
||||
const diffYaw = this.absYaw - this.lastAbsYaw;
|
||||
const diffPitch = this.absPitch - this.lastAbsPitch;
|
||||
|
||||
const doRelativeMove = Math.abs(diffX) >= 4 || Math.abs(diffY) >= 4 || Math.abs(diffZ) >= 4;
|
||||
const doLook = Math.abs(diffYaw) >= 4 || Math.abs(diffPitch) >= 4;
|
||||
if (Math.abs(diffX) > 128 || Math.abs(diffY) > 128 || Math.abs(diffZ) > 128) {
|
||||
this.world.sendToNearbyClients(this, new PacketEntityTeleport(this.entityId, this.absX, this.absY, this.absZ, this.absYaw, this.absPitch).writeData());
|
||||
} else if (doRelativeMove && doLook) {
|
||||
this.world.sendToNearbyClients(this, new PacketEntityLookRelativeMove(this.entityId, diffX, diffY, diffZ, this.absYaw, this.absPitch).writeData());
|
||||
} else if (doRelativeMove) {
|
||||
this.world.sendToNearbyClients(this, new PacketEntityRelativeMove(this.entityId, diffX, diffY, diffZ).writeData());
|
||||
} else if (doLook) {
|
||||
this.world.sendToNearbyClients(this, new PacketEntityLook(this.entityId, this.absYaw, this.absPitch).writeData());
|
||||
}
|
||||
|
||||
if (doRelativeMove) {
|
||||
this.lastAbsX = this.absX;
|
||||
this.lastAbsY = this.absY;
|
||||
this.lastAbsZ = this.absZ;
|
||||
}
|
||||
if (doLook) {
|
||||
this.lastAbsYaw = this.absYaw;
|
||||
this.lastAbsPitch = this.absPitch;
|
||||
}
|
||||
return this.world.getChunkBlockId(this.chunk, this.position.x, this.position.y + this.headHeight, this.position.z) === Block.waterStill.blockId;
|
||||
}
|
||||
|
||||
onTick() {
|
||||
super.onTick();
|
||||
this.sendPositionUpdate();
|
||||
|
||||
if (!this.onGround) {
|
||||
this.fallDistance
|
||||
|
@ -111,8 +58,5 @@ export class EntityLiving extends Entity {
|
|||
} else {
|
||||
this.timeInWater = Number.MIN_SAFE_INTEGER;
|
||||
}
|
||||
|
||||
this.lastYaw = this.yaw;
|
||||
this.lastPitch = this.pitch;
|
||||
}
|
||||
}
|
|
@ -1,11 +1,10 @@
|
|||
import { Vec3 } from "../Vec3"
|
||||
|
||||
export interface IEntity {
|
||||
entityId:number,
|
||||
x:number,
|
||||
y:number,
|
||||
z:number,
|
||||
lastX:number,
|
||||
lastY:number,
|
||||
lastZ:number,
|
||||
position:Vec3,
|
||||
lastPosition:Vec3,
|
||||
velocity:Vec3,
|
||||
crouching:boolean,
|
||||
updateMetadata:() => void,
|
||||
distanceTo:(entity:IEntity) => number,
|
||||
|
|
|
@ -9,6 +9,8 @@ import { PacketUpdateHealth } from "../packets/UpdateHealth";
|
|||
import { Inventory } from "../inventories/Inventory";
|
||||
import { ItemStack } from "../inventories/ItemStack";
|
||||
import { Block } from "../blocks/Block";
|
||||
import PlayerInventory from "../inventories/PlayerInventory";
|
||||
import { Item } from "../items/Item";
|
||||
|
||||
const CHUNK_LOAD_RANGE = 5;
|
||||
|
||||
|
@ -19,7 +21,7 @@ export class Player extends EntityLiving {
|
|||
public loadedChunks:Array<number>;
|
||||
public justUnloaded:Array<number>;
|
||||
public mpClient?:MPClient;
|
||||
public inventory:Inventory;
|
||||
public inventory:PlayerInventory;
|
||||
|
||||
private lastHealth:number;
|
||||
|
||||
|
@ -30,16 +32,16 @@ export class Player extends EntityLiving {
|
|||
this.loadedChunks = new Array<number>();
|
||||
this.justUnloaded = new Array<number>();
|
||||
|
||||
this.inventory = new Inventory(44, "Player Inventory");
|
||||
this.inventory = new PlayerInventory();
|
||||
|
||||
this.inventory.setSlotItemStack(36, new ItemStack(Block.dirt, 1));
|
||||
this.inventory.setSlotItemStack(37, new ItemStack(Block.dirt, 2));
|
||||
this.inventory.setSlotItemStack(38, new ItemStack(Block.dirt, 3));
|
||||
this.inventory.setSlotItemStack(36, new ItemStack(Item.ironSword, 1));
|
||||
this.inventory.setSlotItemStack(37, new ItemStack(Item.ironPickaxe, 1));
|
||||
this.inventory.setSlotItemStack(38, new ItemStack(Item.ironShovel, 1));
|
||||
this.inventory.setSlotItemStack(39, new ItemStack(Item.ironAxe, 1));
|
||||
this.inventory.setSlotItemStack(43, new ItemStack(Block.dirt, 32));
|
||||
|
||||
this.username = username;
|
||||
this.x = 8;
|
||||
this.y = 64;
|
||||
this.z = 8;
|
||||
this.position.set(8, 64, 8);
|
||||
|
||||
this.lastHealth = this.health;
|
||||
}
|
||||
|
@ -49,9 +51,9 @@ export class Player extends EntityLiving {
|
|||
}
|
||||
|
||||
private async updatePlayerChunks() {
|
||||
const bitX = this.x >> 4;
|
||||
const bitZ = this.z >> 4;
|
||||
if (bitX != this.lastX >> 4 || bitZ != this.lastZ >> 4 || this.firstUpdate) {
|
||||
const bitX = this.position.x >> 4;
|
||||
const bitZ = this.position.z >> 4;
|
||||
if (bitX != this.lastPosition.x >> 4 || bitZ != this.lastPosition.z >> 4 || this.firstUpdate) {
|
||||
if (this.firstUpdate) {
|
||||
this.firstUpdate = false;
|
||||
// TODO: Make this based on the player's initial coords
|
||||
|
|
|
@ -3,7 +3,7 @@ import { ItemStack } from "./ItemStack";
|
|||
import IInventory from "./IInventory";
|
||||
|
||||
export class Inventory implements IInventory {
|
||||
private itemStacks:Array<ItemStack | null>;
|
||||
public itemStacks:Array<ItemStack | null>;
|
||||
|
||||
private size:number;
|
||||
private name:string;
|
||||
|
@ -18,6 +18,20 @@ export class Inventory implements IInventory {
|
|||
this.name = name;
|
||||
}
|
||||
|
||||
addItemStack(itemStack:ItemStack) {
|
||||
// Check bottom inventory row (hotbar) first.
|
||||
/*let workingItemStack:ItemStack | null;
|
||||
for (let slotId = 9; slotId <= 35; slotId++) {
|
||||
if (itemStack.size === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ((workingItemStack = this.itemStacks[slotId]) != null) {
|
||||
workingItemStack.insert(itemStack);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
getInventoryName() {
|
||||
return this.name;
|
||||
}
|
||||
|
|
|
@ -3,13 +3,15 @@ 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;
|
||||
|
||||
public constructor(blockOrItemOrItemID:Block|Item|number, size?:number, damage?:number) {
|
||||
if (blockOrItemOrItemID instanceof Block && size === undefined && damage === undefined) {
|
||||
this.itemID = blockOrItemOrItemID.blockId;
|
||||
this.size = 1;
|
||||
this.size = 0;
|
||||
this.damage = 0;
|
||||
} else if (blockOrItemOrItemID instanceof Block && typeof(size) === "number" && damage === undefined) {
|
||||
this.itemID = blockOrItemOrItemID.blockId;
|
||||
|
@ -21,7 +23,7 @@ export class ItemStack {
|
|||
this.damage = damage;
|
||||
} else if (blockOrItemOrItemID instanceof Item && size === undefined && damage === undefined) {
|
||||
this.itemID = blockOrItemOrItemID.shiftedItemID;
|
||||
this.size = 1;
|
||||
this.size = 0;
|
||||
this.damage = 0;
|
||||
} else if (blockOrItemOrItemID instanceof Item && typeof(size) === "number" && damage === undefined) {
|
||||
this.itemID = blockOrItemOrItemID.shiftedItemID;
|
||||
|
@ -35,9 +37,43 @@ export class ItemStack {
|
|||
this.itemID = blockOrItemOrItemID;
|
||||
this.size = size;
|
||||
this.damage = damage;
|
||||
} else if (typeof(blockOrItemOrItemID) === "number" && typeof(size) === "number" && damage === undefined) {
|
||||
this.itemID = blockOrItemOrItemID;
|
||||
this.size = size;
|
||||
this.damage = 0;
|
||||
} else if (typeof(blockOrItemOrItemID) === "number" && size === undefined && damage === undefined) {
|
||||
this.itemID = blockOrItemOrItemID;
|
||||
this.size = 0;
|
||||
this.damage = 0;
|
||||
} else {
|
||||
throw new Error(`ItemStack created with invalid properties (${typeof(blockOrItemOrItemID)}, ${typeof(size)}, ${typeof(damage)})`);
|
||||
}
|
||||
|
||||
this.isBlock = this.itemID < 256;
|
||||
this.maxSize = !this.isBlock ? Item.getByShiftedItemId(this.itemID).maxStackSize : 64;
|
||||
}
|
||||
|
||||
public insert(itemStack:ItemStack) {
|
||||
const remainingSpace = this.spaceAvaliable;
|
||||
if (remainingSpace === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (remainingSpace >= itemStack.size) {
|
||||
this.size += itemStack.size;
|
||||
itemStack.size = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (remainingSpace < itemStack.size) {
|
||||
this.size += remainingSpace;
|
||||
itemStack.size -= remainingSpace;
|
||||
}
|
||||
}
|
||||
|
||||
public get spaceAvaliable() {
|
||||
// Stack size check for Item(s) and Block(s).
|
||||
return Math.max(this.maxSize - this.size, 0);
|
||||
}
|
||||
|
||||
split(amount:number) {
|
||||
|
|
69
server/inventories/PlayerInventory.ts
Normal file
69
server/inventories/PlayerInventory.ts
Normal file
|
@ -0,0 +1,69 @@
|
|||
import { Inventory } from "./Inventory";
|
||||
import { ItemStack } from "./ItemStack";
|
||||
|
||||
export default class PlayerInventory extends Inventory {
|
||||
public constructor() {
|
||||
super(44, "Player Inventory");
|
||||
}
|
||||
|
||||
addItemStack(itemStack:ItemStack) {
|
||||
const itemStacksOfSameType:Array<ItemStack> = new Array<ItemStack>();
|
||||
|
||||
// Check bottom inventory row (hotbar) first.
|
||||
let workingItemStack:ItemStack | null;
|
||||
for (let slotId = 36; slotId <= 44; slotId++) {
|
||||
if ((workingItemStack = this.itemStacks[slotId]) != null) {
|
||||
itemStacksOfSameType.push(workingItemStack);
|
||||
}
|
||||
}
|
||||
|
||||
for (let slotId = 9; slotId <= 35; slotId++) {
|
||||
if ((workingItemStack = this.itemStacks[slotId]) != null) {
|
||||
itemStacksOfSameType.push(workingItemStack);
|
||||
}
|
||||
}
|
||||
|
||||
// Insert into existing stacks first.
|
||||
for (const inventoryItemStack of itemStacksOfSameType) {
|
||||
// Exit early if we have nothing left
|
||||
if (itemStack.size === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (inventoryItemStack.itemID !== itemStack.itemID || inventoryItemStack.damage !== itemStack.damage) {
|
||||
continue;
|
||||
}
|
||||
|
||||
inventoryItemStack.insert(itemStack);
|
||||
}
|
||||
|
||||
// Exit early if we have nothing left
|
||||
if (itemStack.size === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let slotId = 36; slotId <= 44; slotId++) {
|
||||
// Exit early if we have nothing left
|
||||
if (itemStack.size === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((workingItemStack = this.itemStacks[slotId]) == null) {
|
||||
const stack = this.itemStacks[slotId] = new ItemStack(itemStack.itemID, 0, itemStack.damage);
|
||||
stack.insert(itemStack);
|
||||
}
|
||||
}
|
||||
|
||||
for (let slotId = 9; slotId <= 35; slotId++) {
|
||||
// Exit early if we have nothing left
|
||||
if (itemStack.size === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((workingItemStack = this.itemStacks[slotId]) == null) {
|
||||
const stack = this.itemStacks[slotId] = new ItemStack(itemStack.itemID, 0, itemStack.damage);
|
||||
stack.insert(itemStack);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
3
server/items/IItemBehaviour.ts
Normal file
3
server/items/IItemBehaviour.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
export interface IItemBehaviour {
|
||||
|
||||
}
|
|
@ -1,10 +1,20 @@
|
|||
export class Item {
|
||||
public static items:Array<Item> = new Array<Item>();
|
||||
|
||||
public maxStackSize:number;
|
||||
public shiftedItemID:number;
|
||||
public name:string;
|
||||
|
||||
public constructor(itemID:number) {
|
||||
this.shiftedItemID = 256 + itemID;
|
||||
this.maxStackSize = 64;
|
||||
this.name = "UNNAMED";
|
||||
|
||||
Item.items[itemID] = this;
|
||||
}
|
||||
|
||||
public static getByShiftedItemId(shiftedItemID:number) {
|
||||
return Item.items[shiftedItemID - 256];
|
||||
}
|
||||
|
||||
public setMaxStackSize(stackSize:number) {
|
||||
|
@ -12,5 +22,19 @@ export class Item {
|
|||
return this;
|
||||
}
|
||||
|
||||
public setName(name:string) {
|
||||
this.name = name;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
// 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");
|
||||
}
|
Loading…
Reference in a new issue