Skip to content

Commit

Permalink
chore(world): improved entity rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
PMK744 committed Aug 29, 2024
1 parent d4a42e8 commit 4f1810f
Show file tree
Hide file tree
Showing 8 changed files with 251 additions and 218 deletions.
6 changes: 6 additions & 0 deletions packages/serenity/src/providers/leveldb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,12 @@ class LevelDBProvider extends WorldProvider {
this.writeEntity(entity);
}

// Iterate through the players and write them to the database.
for (const player of dimension.getPlayers()) {
// Write the player to the database.
this.writePlayer(player);
}

// Get the block data from the dimension.
const blockData = [...dimension.blocks.values()].map((x) =>
Block.serialize(x)
Expand Down
146 changes: 141 additions & 5 deletions packages/world/src/components/player/entity-rendering.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
import { EntityIdentifier } from "@serenityjs/entity";
import {
AbilityLayerType,
AbilitySet,
AddEntityPacket,
AddItemActorPacket,
AddPlayerPacket,
NetworkItemStackDescriptor,
PropertySyncData,
RemoveEntityPacket
} from "@serenityjs/protocol";

import { PlayerComponent } from "./player-component";
import { PlayerStatus, type Player } from "../../player";
import { ItemStack } from "../../item";

import type { Player } from "../../player";
import { PlayerComponent } from "./player-component";

class PlayerEntityRenderingComponent extends PlayerComponent {
public static readonly identifier = "minecraft:entity_rendering";
Expand All @@ -19,19 +30,144 @@ class PlayerEntityRenderingComponent extends PlayerComponent {
}

public onTick(): void {
// Check if the player is spawned
if (this.player.status !== PlayerStatus.Spawned) return;

// Get all the entities in the player's dimension
const entities = this.player.dimension.entities;

// Iterate over the entities
for (const [unique, entity] of entities) {
// Check if the entity is the player or if the entity has already been rendered
if (entity === this.player || this.entities.has(unique)) continue;
if (this.entities.has(unique) || entity === this.player) continue;

// Check if the entity is a player and if the player is spawned
if (entity.isPlayer() && entity.status !== PlayerStatus.Spawned) continue;

// Add the entity to the rendered entities
this.entities.add(unique);

// Spawn the entity for the player
entity.spawn(this.player);
// Check if the entity is a player
if (entity.isPlayer()) {
// Create a new AddPlayerPacket
const packet = new AddPlayerPacket();

// Get the players inventory
const inventory = entity.getComponent("minecraft:inventory");

// Get the players held item
const heldItem = inventory.getHeldItem();

// Set the packet properties
packet.uuid = entity.uuid;
packet.username = entity.username;
packet.runtimeId = entity.runtime;
packet.platformChatId = String(); // TODO: Not sure what entity is.
packet.position = entity.position;
packet.velocity = entity.velocity;
packet.pitch = entity.rotation.pitch;
packet.yaw = entity.rotation.yaw;
packet.headYaw = entity.rotation.headYaw;
packet.heldItem =
heldItem === null
? new NetworkItemStackDescriptor(0)
: ItemStack.toNetworkStack(heldItem);
packet.gamemode = entity.gamemode;
packet.data = [...entity.metadata];
packet.properties = new PropertySyncData([], []);
packet.uniqueEntityId = entity.unique;
packet.premissionLevel = entity.permission;
packet.commandPermission = entity.permission === 2 ? 1 : 0;
packet.abilities = [
{
type: AbilityLayerType.Base,
abilities: [...entity.abilities.entries()].map(
([ability, value]) => new AbilitySet(ability, value)
),
walkSpeed: 0.1,
flySpeed: 0.05
}
];
packet.links = [];
packet.deviceId = "";
packet.deviceOS = 0;

// Send the packet to the player
this.player.session.send(packet);

// Sync the entity
entity.sync();

// Continue to the next entity
continue;
}

// Check if the entity is an item
if (entity.isItem()) {
// Get the item component
const itemComponent = entity.getComponent("minecraft:item");

// Create a new AddItemActorPacket
const packet = new AddItemActorPacket();

// Set the packet properties
packet.uniqueId = entity.unique;
packet.runtimeId = entity.runtime;
packet.item = ItemStack.toNetworkStack(itemComponent.itemStack);
packet.position = entity.position;
packet.velocity = entity.velocity;
packet.data = [...entity.metadata];
packet.fromFishing = false;

// Send the packet to the player
this.player.session.send(packet);

// Sync the entity
entity.sync();

// Continue to the next entity
continue;
}

// Create a new AddEntityPacket
const packet = new AddEntityPacket();

// Set the packet properties
packet.uniqueEntityId = entity.unique;
packet.runtimeId = entity.runtime;
packet.identifier = entity.type.identifier;
packet.position = entity.position;
packet.velocity = entity.velocity;
packet.pitch = entity.rotation.pitch;
packet.yaw = entity.rotation.yaw;
packet.headYaw = entity.rotation.headYaw;
packet.bodyYaw = entity.rotation.yaw;
packet.attributes = [];
packet.data = [...entity.metadata];
packet.properties = new PropertySyncData([], []);
packet.links = [];

// Send the packet to the player
this.player.session.send(packet);

// Sync the entity
entity.sync();
}

// Iterate over the entities
for (const unique of this.entities) {
// Check if the entity is still in the player's dimension
if (this.player.dimension.entities.has(unique)) continue;

// Create a new remove entity packet
const packet = new RemoveEntityPacket();
packet.uniqueEntityId = unique;

// Send the packet to the player
this.player.session.send(packet);

// Remove the entity from the rendered entities
this.entities.delete(unique);
}
}
}
Expand Down
1 change: 1 addition & 0 deletions packages/world/src/components/player/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export * from "./chunk-rendering";
export * from "./entity-rendering";
export * from "./attribute";
export * from "./crafting-input";
export * from "./list";
84 changes: 84 additions & 0 deletions packages/world/src/components/player/list.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import {
PlayerListAction,
PlayerListPacket,
PlayerListRecord
} from "@serenityjs/protocol";
import { EntityIdentifier } from "@serenityjs/entity";

import { PlayerStatus, type Player } from "../../player";

import { PlayerComponent } from "./player-component";

class PlayerListComponent extends PlayerComponent {
public static readonly identifier = "minecraft:player_list";

public static readonly types = [EntityIdentifier.Player];

/**
* The collective set of all the players that are displayed in the player list.
*/
public readonly players: Set<string> = new Set();

public constructor(player: Player) {
super(player, PlayerListComponent.identifier);
}

public onTick(): void {
// Check if the player is spawned
if (this.player.status !== PlayerStatus.Spawned) return;

// Get the current tick of the world
const currentTick = this.player.dimension.world.currentTick;

// Check if the current tick is divisible by 20
// We don't need this running every tick
if (currentTick % 20n !== 0n) return;

// Get all the players in the player's dimension
const players = this.player.dimension.getPlayers();

// Filter out the players that are already in the player list & if the player is spawned
const adding = players.filter(
(player) =>
!this.players.has(player.uuid) && player.status === PlayerStatus.Spawned
);

// Create a new player list packet for the players that need to be added to the player list
const add = new PlayerListPacket();
add.action = PlayerListAction.Add;
add.records = adding.map((player) => ({
uniqueId: player.unique,
uuid: player.uuid,
xuid: player.xuid,
username: player.username,
skin: player.skin,
platformBuild: player.device.os,
platformChatIdentifier: "",
isHost: false,
isVisitor: false,
isTeacher: false
}));

// Filter out the players that need to be removed from the player list
const removing = [...this.players].filter(
(uuid) => !players.some((x) => x.uuid === uuid)
);

// Create a new player list packet for the players that need to be removed from the player list
const remove = new PlayerListPacket();
remove.action = PlayerListAction.Remove;
remove.records = removing.map((uuid) => new PlayerListRecord(uuid));

// Send the player list packets to the player
if (add.records.length > 0) this.player.session.send(add);
if (remove.records.length > 0) this.player.session.send(remove);

// Update the player list
for (const player of adding) this.players.add(player.uuid);

// Update the player list
for (const uuid of removing) this.players.delete(uuid);
}
}

export { PlayerListComponent };
Loading

0 comments on commit 4f1810f

Please sign in to comment.