-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create the design explaining how prismarine-world will contain all the state of mineflayer and flying-squid #1
Comments
kind of related PrismarineJS/node-minecraft-protocol#231 |
PrismarineJS/mineflayer#334 (comment) this comment is describing a lot of tasks to do |
@TheDudeFromCI @Karang I made a draft for this design there ^ |
Working on the first draft for this in #2 Should be ready soon, but still lots more information to add before it's ready for a review and merge. |
a distribution using bounding volume hierarchy (octree, kd-tree ...) works when the entities are spread on the world but what if there is 1 M entities in a small region ? |
related PrismarineJS/mineflayer#1151 |
@louis030195 yes handling scaling of many entities in one chunk is much harder |
How this should work?For example, two groups of players (from two continents) are in same Minecraft world, with 24 chunk render distance.
|
prismarinejsA monorepo to help keep everything maintained and updated.
prismarinejs@reality
An module for intelligent load distribution across processes and/or remote instances and latency minimization. declare class RealityManager extends EventEmitter {
idleProcesses: number = 1;
maxProcesses: number = 4;
allowReconnect: boolean = true; // Keep reconnecting to dead servers
maxInstances: number = 50; // Nobody will ever need over 50 connections
_instances: Set<RealityInstance>;
/// Events
// When region assigned to this instance
// `data` parameter contains a Buffer of region
on<T = {}>(
event: 'assign',
handler: Function<x: number, z: number, data: Buffer>
): this;
// When this instance was been told to reclaim a region
// Callback function must be called with `true` if region can be reclaimed, `false` otherwise
on<T = {}>(
event: 'reclaim_request',
handler: Function<x: number, z: number, priority: number, callback: function<boolean>>
): this;
/// Soft transfer of regions
// This method assigns a region to this instance if it isn't assigned yet.
// Otherwise returns an instance assigned to that region.
useRegion(x: number, z: number): Promise<RealityInstance | null>;
// Call this if region is not needed anymore
// This will pass a region to other instance and returns it.
// Otherwise method returns null and region needs to be saved.
freeRegion(x: number, z: number): Promise<RealityInstance | null>;
/// Hard transfer of regions
// This will forcefully assign/reclaim region for exclusive modification
assignRegion(x: number, z: number, inst: RealityInstance): Promise<void>;
reclaimRegion(x: number, z: number, inst: RealityInstance): Promise<void>;
}
declare class RealityInstance {
socket: Socket; // IPC, TCP, QUIC? No matter though
isLocal: boolean; // Do not apply latency measurements
isAlive: boolean; // Is process/server sending stats
_clientLatencyStats: number[]; // Aka ping
_serverLatencyStats: number[]; // Aka TPS
proxifyPlayer(client: Socket): Promise<Socket>; // Proxy client to instance
deproxifyPlayer(client: Socket): Promise<Socket>; // Stop proxying client to instance
} prismarinejs@chunk// A provider class that handles serialization of a chunk across versions.
declare class ChunkProvider {
version: string;
_locks: WeakSet<Chunk>;
fromBuffer(data: Buffer, version?: string = this.version): Block;
toBuffer(block: Block, version?: string = this.version): Buffer;
fromNotch(data: Buffer, version?: string = this.version): Chunk;
toNotch(chunk: Chunk, version?: string = this.version): Buffer;
fromJSON(data: string, version?: string = this.version): Chunk;
toJSON(chunk: Chunk, version?: string = this.version): string;
// Diff chunks per-block
diffChunks (old: Chunk, new: Chunk): Iterator<[position: Vec3, old: Block, new: Block]>;
// Update chunk from part of serialized chunk
fromNotchDiff (old: Chunk, new: Buffer, bitMap: number = 0xFFFF, hasSkyLight: boolean = true, hasBiomes: boolean = true): Chunk;
// Serialize diff of chunks per-section instead of full chunk if possible
toNotchDiff (old: Chunk, new: Chunk): [ new: Buffer, bitMap: number, hasSkyLight: boolean, hasBiomes: boolean ];
};
declare interface ChunkHandler {
toJSON(): string;
fromJSON(data: string): Chunk;
// Using MessagePack for serialization (for web and cross-server interaction)
toBuffer(): Buffer;
fromBuffer(data: Buffer): Chunk;
fromNotch(data: Buffer, bitMap: number = 0xFFFF, hasSkyLight: boolean = true, hasBiomes: boolean = true): Chunk;
toNotch(bitMap: number = 0xFFFF, includeSkyLight: boolean = true, includeBiomes: boolean = true): Buffer;
}
declare class ChunkHandler_PC1_16 implementing ChunkHandler;
declare class ChunkHandler_PC1_15 implementing ChunkHandler;
declare class ChunkHandler_PC1_14 implementing ChunkHandler;
declare class ChunkHandler_PC1_13 implementing ChunkHandler;
declare class ChunkHandler_PC1_09 implementing ChunkHandler;
declare class ChunkHandler_PC1_08 implementing ChunkHandler;
declare class ChunkHandler_PE1_00 implementing ChunkHandler;
declare class ChunkHandler_PE0_14 implementing ChunkHandler;
// An abstraction of chunk.
// (Actual data and most operations can be sent over network!)
declare class Chunk {
id: Vec2;
hasSkyLight: boolean;
hasBiomes: boolean;
_rawSkyLight: Buffer;
_rawBiome: Buffer;
_provider: WeakRef<ChunkProvider>;
// For unparralleable/atomic operations on chunks
// Lock *will* prevent chunk from saving or updating
get isLocked() : Promise<boolean>;
wait(): Promise<void>;
lock(): Promise<void>;
unlock(): Promise<void>;
getBlock(pos: Vec3): Promise<Block>;
setBlock(pos: Vec3, block: Block): Promise<void>;
// Aliases to chunk accessors, position can be relative to chunk/world
getBiome(pos: Vec3): Promise<Biome>;
setBiome(pos: Vec3, biome: Biome): Promise<void>;
getBlockLight(pos: Vec3): Promise<number>;
setBlockLight(pos: Vec3, light: number): Promise<void>;
getSkyLight(pos: Vec3): Promise<number>;
setSkyLight(pos: Vec3, light: number): Promise<void>;
}; prismarinejs@block// A provider class that handles block metadata caching and block serialization across versions.
declare class BlockProvider {
version: string;
fromBuffer(data: Buffer, version?: string = this.version): Block;
toBuffer(block: Block, version?: string = this.version): Buffer;
fromJSON(data: string, version?: string = this.version): Block;
toJSON(block: Block, version?: string = this.version): string;
_metadataCache: WeakMap<Block, object>;
getMetadata(id: number): Promise<object>;
};
// Block bounding box
declare enum BoundingBoxEnum {
Empty,
Full,
Half,
Stairs,
Liquid
};
// An object for storing block state
declare class BlockState extends Object {
toJSON(): string;
static fromJSON(data: string): BlockState;
toNBT(): Buffer;
static fromNBT(data: Buffer): BlockState;
// Using MessagePack for serialization (for web and cross-server interaction)
toBuffer(): Buffer;
static fromBuffer(data: Buffer): BlockState;
};
// An abstraction of a block (aka struct). Stores basic block data, weak references and empty `BlockState`.
declare class Block {
id: number;
position: Vec3;
// Chunk data (lazy-loaded)
_chunk: WeakRef<Chunk>;
get biome(): Promise<Biome>;
set biome(value?: Biome): Promise<void>;
get light(): Promise<?number>;
set light(value?: number): Promise<void>;
get skyLight(): Promise<?number>;
set skyLight(value?: number): Promise<void>;
// State and its aliases (lazy-loaded)
_state: BlockState;
get state(): Promise<BlockState>;
get displayName(): Promise<string>;
set displayName(value?: string): Promise<void>;
get signText(): Promise<?string>;
set signText(value?: string): Promise<void>;
// Metadata (lazy-loaded)
_provider: WeakRef<BlockProvider>;
get name(): Promise<string>;
get hardness(): Promise<number>;
get boundingBox(): Promise<BoundingBoxEnum>;
get diggable(): Promise<boolean>;
get material(): Promise<?string>;
get transparent(): Promise<boolean>;
get harvestTools(): Promise<?{ [k: string]: number }>;
get drops(): Promise<?Array<{ minCount?: number, maxCount?: number, drop: number | { id: number, metadata: number } }>>;
// Methods using metadata
canHarvest(heldItemType: number | null): Promise<boolean>;
digTime(heldItemType: number | null, inWater: boolean, inMidair: boolean, enchantments?: Enchantment[], effects?: Effect[]): Promise<number>;
}; prismarinejs@biomedeclare class BiomeProvider {
version: string;
getBiome(id: number, version?: string = this.version): Promise<Biome>;
_metadataCache: WeakMap<Block, object>;
getMetadata(id: number): Promise<object>;
}
declare class Biome {
id: number;
name: string;
color?: number;
displayName?: string;
rainfall: number;
temperature: number;
height?: number | null;
} |
Interesting ideas. I disagree with the monorepo part (because it encourages tight coupling between packages, ie discourage really independent packages) What I want to introduce (and described above) is a state store which is prismarine-world. That will make it possible to not have monolithic minecraft servers but instead a bunch of small services handling various things. That will also make it possible to distribute them however is required. This is already ongoing btw but will need some more work to finish it. See PrismarineJS/mineflayer#334 (comment) |
Just to clarify: this issue is already done. The work that needs to be done before we can move further on this is extending prismarine-world. |
|
PrismarineJS/flying-squid#392
PrismarineJS/mineflayer#334
Basic idea is pworld containing 100% of the state
pworld server providing data to pworld client
pviewer using pworld client to render data
pworld being using in flying-squid and mineflayer
mineflayer and flying-squid being stateless transformers on top of a pworld and network
The text was updated successfully, but these errors were encountered: