Initial TypeScript commit
This commit is contained in:
parent
d730e8f115
commit
3edcbc062f
80 changed files with 4696 additions and 2514 deletions
2
.gitattributes
vendored
2
.gitattributes
vendored
|
@ -1,2 +0,0 @@
|
||||||
# Auto detect text files and perform LF normalization
|
|
||||||
* text=auto
|
|
108
.gitignore
vendored
108
.gitignore
vendored
|
@ -1,109 +1 @@
|
||||||
# Custom stuff
|
|
||||||
old/
|
|
||||||
|
|
||||||
#===- Default node gitignore -===#
|
|
||||||
|
|
||||||
# Logs
|
|
||||||
logs
|
|
||||||
*.log
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
lerna-debug.log*
|
|
||||||
|
|
||||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
|
||||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
|
||||||
|
|
||||||
# Runtime data
|
|
||||||
pids
|
|
||||||
*.pid
|
|
||||||
*.seed
|
|
||||||
*.pid.lock
|
|
||||||
|
|
||||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
|
||||||
lib-cov
|
|
||||||
|
|
||||||
# Coverage directory used by tools like istanbul
|
|
||||||
coverage
|
|
||||||
*.lcov
|
|
||||||
|
|
||||||
# nyc test coverage
|
|
||||||
.nyc_output
|
|
||||||
|
|
||||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
|
||||||
.grunt
|
|
||||||
|
|
||||||
# Bower dependency directory (https://bower.io/)
|
|
||||||
bower_components
|
|
||||||
|
|
||||||
# node-waf configuration
|
|
||||||
.lock-wscript
|
|
||||||
|
|
||||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
|
||||||
build/Release
|
|
||||||
|
|
||||||
# Dependency directories
|
|
||||||
node_modules/
|
node_modules/
|
||||||
jspm_packages/
|
|
||||||
|
|
||||||
# TypeScript v1 declaration files
|
|
||||||
typings/
|
|
||||||
|
|
||||||
# TypeScript cache
|
|
||||||
*.tsbuildinfo
|
|
||||||
|
|
||||||
# Optional npm cache directory
|
|
||||||
.npm
|
|
||||||
|
|
||||||
# Optional eslint cache
|
|
||||||
.eslintcache
|
|
||||||
|
|
||||||
# Microbundle cache
|
|
||||||
.rpt2_cache/
|
|
||||||
.rts2_cache_cjs/
|
|
||||||
.rts2_cache_es/
|
|
||||||
.rts2_cache_umd/
|
|
||||||
|
|
||||||
# Optional REPL history
|
|
||||||
.node_repl_history
|
|
||||||
|
|
||||||
# Output of 'npm pack'
|
|
||||||
*.tgz
|
|
||||||
|
|
||||||
# Yarn Integrity file
|
|
||||||
.yarn-integrity
|
|
||||||
|
|
||||||
# dotenv environment variables file
|
|
||||||
.env
|
|
||||||
.env.test
|
|
||||||
|
|
||||||
# parcel-bundler cache (https://parceljs.org/)
|
|
||||||
.cache
|
|
||||||
|
|
||||||
# Next.js build output
|
|
||||||
.next
|
|
||||||
|
|
||||||
# Nuxt.js build / generate output
|
|
||||||
.nuxt
|
|
||||||
dist
|
|
||||||
|
|
||||||
# Gatsby files
|
|
||||||
.cache/
|
|
||||||
# Comment in the public line in if your project uses Gatsby and *not* Next.js
|
|
||||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
|
||||||
# public
|
|
||||||
|
|
||||||
# vuepress build output
|
|
||||||
.vuepress/dist
|
|
||||||
|
|
||||||
# Serverless directories
|
|
||||||
.serverless/
|
|
||||||
|
|
||||||
# FuseBox cache
|
|
||||||
.fusebox/
|
|
||||||
|
|
||||||
# DynamoDB Local files
|
|
||||||
.dynamodb/
|
|
||||||
|
|
||||||
# TernJS port file
|
|
||||||
.tern-port
|
|
||||||
|
|
13
README.md
13
README.md
|
@ -3,21 +3,20 @@ me trying to understand minecraft beta's protocol
|
||||||
|
|
||||||
**Implemented:**
|
**Implemented:**
|
||||||
- Basic flat terrain generation
|
- Basic flat terrain generation
|
||||||
|
|
||||||
|
**WIP:**
|
||||||
|
|
||||||
|
**To Implement:**
|
||||||
- Terrain sending
|
- Terrain sending
|
||||||
- Block placement
|
- Block placement
|
||||||
- Block breaking
|
- Block breaking
|
||||||
- Entities:
|
- Entities:
|
||||||
- Players
|
- Players
|
||||||
|
|
||||||
**WIP:**
|
|
||||||
- Terrain generation using perlin noise
|
|
||||||
- Cross chunk structure generation (trees, buildings, etc...)
|
|
||||||
|
|
||||||
**To Implement:**
|
|
||||||
- Entities:
|
|
||||||
- Items/Blocks
|
- Items/Blocks
|
||||||
- Animals
|
- Animals
|
||||||
- Mobs
|
- Mobs
|
||||||
|
- Terrain generation using perlin noise
|
||||||
|
- Cross chunk structure generation (trees, buildings, etc...)
|
||||||
- Inventories
|
- Inventories
|
||||||
- Terrain saving to disk
|
- Terrain saving to disk
|
||||||
- Sleeping in beds
|
- Sleeping in beds
|
||||||
|
|
206
bufferStuff.ts
Normal file
206
bufferStuff.ts
Normal file
|
@ -0,0 +1,206 @@
|
||||||
|
export class Reader {
|
||||||
|
private buffer:Buffer;
|
||||||
|
private offset:number;
|
||||||
|
|
||||||
|
public constructor(buffer:Buffer) {
|
||||||
|
this.buffer = buffer;
|
||||||
|
this.offset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readByte() {
|
||||||
|
const value = this.buffer.readInt8(this.offset);
|
||||||
|
this.offset++;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readUByte() {
|
||||||
|
const value = this.buffer.readUInt8(this.offset);
|
||||||
|
this.offset++;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readBool() {
|
||||||
|
return Boolean(this.readUByte());
|
||||||
|
}
|
||||||
|
|
||||||
|
public readShort() {
|
||||||
|
const value = this.buffer.readInt16BE(this.offset);
|
||||||
|
this.offset += 2
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readInt() {
|
||||||
|
const value = this.buffer.readInt32BE(this.offset);
|
||||||
|
this.offset += 4;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readLong() {
|
||||||
|
const value = this.buffer.readBigInt64BE(this.offset);
|
||||||
|
this.offset += 8;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readFloat() {
|
||||||
|
const value = this.buffer.readFloatBE(this.offset);
|
||||||
|
this.offset += 4;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readDouble() {
|
||||||
|
const value = this.buffer.readDoubleBE(this.offset);
|
||||||
|
this.offset += 8;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readString() {
|
||||||
|
const length = this.readShort();
|
||||||
|
let text:string = "";
|
||||||
|
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
text += String.fromCharCode(this.readShort());
|
||||||
|
}
|
||||||
|
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Writer {
|
||||||
|
private buffer:Buffer;
|
||||||
|
private offset:number;
|
||||||
|
private resizable:boolean;
|
||||||
|
|
||||||
|
public constructor(size:number = 0) {
|
||||||
|
this.buffer = Buffer.alloc(size);
|
||||||
|
this.offset = 0;
|
||||||
|
this.resizable = size === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public toBuffer() {
|
||||||
|
return this.buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public toString() {
|
||||||
|
return this.buffer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public writeBuffer(buffer:Buffer) {
|
||||||
|
this.buffer = Buffer.concat([this.buffer, buffer], this.buffer.length + buffer.length);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public writeByte(value:number) {
|
||||||
|
if (this.resizable) {
|
||||||
|
const buffer = Buffer.alloc(1);
|
||||||
|
buffer.writeInt8(value);
|
||||||
|
this.writeBuffer(buffer);
|
||||||
|
} else {
|
||||||
|
this.buffer.writeInt8(value, this.offset);
|
||||||
|
this.offset++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public writeUByte(value:number) {
|
||||||
|
if (this.resizable) {
|
||||||
|
const buffer = Buffer.alloc(1);
|
||||||
|
buffer.writeUInt8(value);
|
||||||
|
this.writeBuffer(buffer);
|
||||||
|
} else {
|
||||||
|
this.buffer.writeUInt8(value, this.offset);
|
||||||
|
this.offset++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public writeBool(value:boolean|number) {
|
||||||
|
if (typeof(value) === "number") {
|
||||||
|
value = Boolean(value);
|
||||||
|
}
|
||||||
|
this.writeUByte(value ? 1 : 0);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public writeShort(value:number) {
|
||||||
|
if (this.resizable) {
|
||||||
|
const buffer = Buffer.alloc(2);
|
||||||
|
buffer.writeInt16BE(value);
|
||||||
|
this.writeBuffer(buffer);
|
||||||
|
} else {
|
||||||
|
this.buffer.writeInt16BE(value, this.offset);
|
||||||
|
this.offset += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public writeInt(value:number) {
|
||||||
|
if (this.resizable) {
|
||||||
|
const buffer = Buffer.alloc(4);
|
||||||
|
buffer.writeInt32BE(value);
|
||||||
|
this.writeBuffer(buffer);
|
||||||
|
} else {
|
||||||
|
this.buffer.writeInt32BE(value, this.offset);
|
||||||
|
this.offset += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public writeLong(value:number|bigint) {
|
||||||
|
if (typeof(value) !== "bigint") {
|
||||||
|
value = BigInt(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.resizable) {
|
||||||
|
const buffer = Buffer.alloc(8);
|
||||||
|
buffer.writeBigInt64BE(value);
|
||||||
|
this.writeBuffer(buffer);
|
||||||
|
} else {
|
||||||
|
this.buffer.writeBigInt64BE(value, this.offset);
|
||||||
|
this.offset += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public writeFloat(value:number) {
|
||||||
|
if (this.resizable) {
|
||||||
|
const buffer = Buffer.alloc(4);
|
||||||
|
buffer.writeFloatBE(value);
|
||||||
|
this.writeBuffer(buffer);
|
||||||
|
} else {
|
||||||
|
this.buffer.writeFloatBE(value, this.offset);
|
||||||
|
this.offset += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public writeDouble(value:number) {
|
||||||
|
if (this.resizable) {
|
||||||
|
const buffer = Buffer.alloc(8);
|
||||||
|
buffer.writeDoubleBE(value);
|
||||||
|
this.writeBuffer(buffer);
|
||||||
|
} else {
|
||||||
|
this.buffer.writeDoubleBE(value, this.offset);
|
||||||
|
this.offset += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public writeString(text:string) {
|
||||||
|
this.writeShort(text.length);
|
||||||
|
|
||||||
|
for (let i = 0; i < text.length; i++) {
|
||||||
|
this.writeShort(text.charCodeAt(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
"port": 25565,
|
"port": 25565,
|
||||||
"worldThreads": 4
|
"onlineMode": false,
|
||||||
|
"maxPlayers": 20
|
||||||
}
|
}
|
5
config.ts
Normal file
5
config.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
export interface Config {
|
||||||
|
port: number,
|
||||||
|
onlineMode: boolean,
|
||||||
|
maxPlayers: number
|
||||||
|
}
|
67
console.ts
Normal file
67
console.ts
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
import chalk from "chalk";
|
||||||
|
|
||||||
|
console.clear();
|
||||||
|
|
||||||
|
enum LogType {
|
||||||
|
INFO,
|
||||||
|
WARN,
|
||||||
|
ERROR
|
||||||
|
};
|
||||||
|
|
||||||
|
const LogTags = {
|
||||||
|
INFO: chalk.bgGreen(chalk.black(" INFO ")),
|
||||||
|
BANCHO: chalk.bgMagenta(chalk.black(" BANCHO ")),
|
||||||
|
WEBREQ: chalk.bgGreen(chalk.black(" WEBREQ ")),
|
||||||
|
CHAT: chalk.bgCyan(chalk.black(" CHAT ")),
|
||||||
|
WARN: chalk.bgYellow(chalk.black(" WARN ")),
|
||||||
|
ERROR: chalk.bgRed(" ERRR "),
|
||||||
|
REDIS: chalk.bgRed(chalk.white(" bREDIS ")),
|
||||||
|
STREAM: chalk.bgBlue(chalk.black(" STREAM "))
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
function correctValue(i:number) : string {
|
||||||
|
if (i <= 9) return `0${i}`;
|
||||||
|
else return i.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTime() : string {
|
||||||
|
const time = new Date();
|
||||||
|
return chalk.green(`[${correctValue(time.getHours())}:${correctValue(time.getMinutes())}:${correctValue(time.getSeconds())}]`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function log(tag:string, log:string, logType:LogType = LogType.INFO) : void {
|
||||||
|
switch (logType) {
|
||||||
|
case LogType.INFO:
|
||||||
|
return console.log(`${getTime()} ${tag} ${log}`);
|
||||||
|
case LogType.WARN:
|
||||||
|
return console.warn(`${getTime()} ${tag} ${log}`);
|
||||||
|
case LogType.ERROR:
|
||||||
|
return console.error(`${getTime()} ${tag} ${log}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Console {
|
||||||
|
public static printWebReq(s:string) : void {
|
||||||
|
log(LogTags.WEBREQ, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static printStream(s:string) : void {
|
||||||
|
log(LogTags.STREAM, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static printInfo(s:string) : void {
|
||||||
|
log(LogTags.INFO, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static printChat(s:string) : void {
|
||||||
|
log(LogTags.CHAT, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static printWarn(s:string) : void {
|
||||||
|
log(LogTags.WARN, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static printError(s:string) : void {
|
||||||
|
log(LogTags.ERROR, s);
|
||||||
|
}
|
||||||
|
}
|
73
funkyArray.ts
Normal file
73
funkyArray.ts
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
export class FunkyArray<T, TT> {
|
||||||
|
private items:Map<T, TT> = new Map<T, TT>();
|
||||||
|
private itemKeys:Array<T> = new Array<T>();
|
||||||
|
|
||||||
|
private _getKeys() : Array<T> {
|
||||||
|
const keyArray = new Array<T>();
|
||||||
|
let result:IteratorResult<T, T>;
|
||||||
|
const iterator = this.items.keys();
|
||||||
|
while (!(result = iterator.next()).done) {
|
||||||
|
keyArray.push(result.value);
|
||||||
|
}
|
||||||
|
return keyArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set(key:T, item:TT, regenerate:boolean = true) : TT {
|
||||||
|
this.items.set(key, item);
|
||||||
|
if (regenerate) {
|
||||||
|
this.itemKeys = this._getKeys();
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public remove(key:T, regenerate:boolean = true) {
|
||||||
|
const success = this.items.delete(key);
|
||||||
|
if (regenerate) {
|
||||||
|
this.itemKeys = this._getKeys();
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeFirst(regenerate:boolean = true) {
|
||||||
|
const success = this.items.delete(this.items.keys().next().value);
|
||||||
|
if (regenerate) {
|
||||||
|
this.itemKeys = this._getKeys();
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public first() : TT {
|
||||||
|
return this.items.values().next().value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get length() : number {
|
||||||
|
return this.items.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get(key:T) : TT | undefined {
|
||||||
|
return this.items.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get keys() : Array<T> {
|
||||||
|
return this.itemKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
public forEach(callback: (value:TT) => void) {
|
||||||
|
return new Promise<boolean>((resolve, reject) => {
|
||||||
|
if (this.items.size === 0) {
|
||||||
|
return resolve(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const iterator = this.items.values();
|
||||||
|
let result:IteratorResult<TT, TT>;
|
||||||
|
while (!(result = iterator.next()).done) {
|
||||||
|
callback(result.value);
|
||||||
|
}
|
||||||
|
resolve(true);
|
||||||
|
} catch (e) {
|
||||||
|
reject(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
15
index.js
15
index.js
|
@ -1,15 +0,0 @@
|
||||||
/*
|
|
||||||
==============- index.js -==============
|
|
||||||
Created by Holly (tgpethan) (c) 2021
|
|
||||||
Licenced under MIT
|
|
||||||
========================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
const server = new (require('net').Server)();
|
|
||||||
const config = require("./config.json");
|
|
||||||
|
|
||||||
const mcServer = require("./server/server.js");
|
|
||||||
|
|
||||||
server.listen(config.port, () => mcServer.init(config));
|
|
||||||
|
|
||||||
server.on('connection', mcServer.connection);
|
|
6
index.ts
Normal file
6
index.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import { Config } from "./config";
|
||||||
|
import { readFileSync } from "fs";
|
||||||
|
import { MinecraftServer } from "./server/MinecraftServer";
|
||||||
|
const config:Config = JSON.parse(readFileSync("./config.json").toString()) as Config;
|
||||||
|
|
||||||
|
const mcServer = new MinecraftServer(config);
|
3463
package-lock.json
generated
3463
package-lock.json
generated
File diff suppressed because it is too large
Load diff
20
package.json
20
package.json
|
@ -2,8 +2,13 @@
|
||||||
"name": "mc-beta-server",
|
"name": "mc-beta-server",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.ts",
|
||||||
"scripts": {},
|
"scripts": {
|
||||||
|
"dev:run": "nodemon --watch './**/*.ts' index.ts",
|
||||||
|
"pack": "webpack",
|
||||||
|
"build": "tsc --build",
|
||||||
|
"_clean": "tsc --build --clean"
|
||||||
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/tgpethan/mc-beta-server.git"
|
"url": "git+https://github.com/tgpethan/mc-beta-server.git"
|
||||||
|
@ -16,6 +21,17 @@
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/tgpethan/mc-beta-server#readme",
|
"homepage": "https://github.com/tgpethan/mc-beta-server#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"chalk": "^4.1.0",
|
||||||
"net": "^1.0.2"
|
"net": "^1.0.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^18.15.11",
|
||||||
|
"nodemon": "^2.0.20",
|
||||||
|
"ts-loader": "^9.4.1",
|
||||||
|
"ts-node": "^10.9.1",
|
||||||
|
"typescript": "^4.9.3",
|
||||||
|
"webpack": "^5.75.0",
|
||||||
|
"webpack-cli": "^4.10.0",
|
||||||
|
"webpack-node-externals": "^3.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,86 +0,0 @@
|
||||||
class Block {
|
|
||||||
static blocksList = [];
|
|
||||||
static blockOpaqueList = [];
|
|
||||||
static blockOpacity = [];
|
|
||||||
static blockGrassable = [];
|
|
||||||
static blockContainer = [];
|
|
||||||
|
|
||||||
constructor(blockID = 0) {
|
|
||||||
this.blockID = blockID;
|
|
||||||
|
|
||||||
this.blockHardness = 0;
|
|
||||||
this.blockResistance = 0;
|
|
||||||
this.blockName = "";
|
|
||||||
|
|
||||||
if (Block.blocksList[blockID] != null) {
|
|
||||||
throw `[Block] Slot ${blockID} is already occupied by ${Block.blocksList[blockID]} when adding ID:${blockID}`;
|
|
||||||
} else {
|
|
||||||
Block.blocksList[blockID] = this;
|
|
||||||
Block.blockOpaqueList[blockID] = false; // TODO: Block opaque
|
|
||||||
Block.blockOpacity[blockID] = 0; // TODO: Opacity
|
|
||||||
Block.blockGrassable[blockID] = false; // TODO: Get if block can be grass'd
|
|
||||||
Block.blockContainer[blockID] = false; // TODO: Containers
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setHardness(hardness = 0) {
|
|
||||||
this.blockHardness = hardness;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
setBlockUnbreakable() {
|
|
||||||
this.blockHardness = -1;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
setResistance(resistance = 0) {
|
|
||||||
this.blockHardness = resistance;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
setName(name = "") {
|
|
||||||
this.blockName = name;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
static stone = new Block(1).setHardness(1.5).setResistance(10).setName("Stone");
|
|
||||||
static grass = new Block(2).setHardness(0.6).setName("Grass");
|
|
||||||
static dirt = new Block(3).setHardness(0.5).setName("Dirt");
|
|
||||||
static cobblestone = new Block(4).setHardness(2.0).setResistance(10).setName("Cobblestone");
|
|
||||||
static planks = new Block(5).setHardness(2).setResistance(5).setName("Planks");
|
|
||||||
static sapling = new Block(6).setName("Sapling");
|
|
||||||
static bedrock = new Block(7).setBlockUnbreakable().setResistance(6000000).setName("Bedrock");
|
|
||||||
static waterFlowing = new Block(8).setHardness(100).setName("Flowing Water");
|
|
||||||
static waterStill = new Block(9).setHardness(100).setName("Still Water");
|
|
||||||
static lavaMoving = new Block(10).setHardness(1.5).setResistance(10).setName("Flowing Lava");
|
|
||||||
static lavaStill = new Block(11).setHardness(1.5).setResistance(10).setName("Still Lava");
|
|
||||||
static sand = new Block(12).setHardness(1.5).setResistance(10).setName("Sand");
|
|
||||||
static gravel = new Block(13).setHardness(1.5).setResistance(10).setName("Gravel");
|
|
||||||
static goldOre = new Block(14).setHardness(1.5).setResistance(10).setName("Gold Ore");
|
|
||||||
static ironOre = new Block(15).setHardness(1.5).setResistance(10).setName("Iron Ore");
|
|
||||||
static coalOre = new Block(16).setHardness(1.5).setResistance(10).setName("Coal Ore");
|
|
||||||
static wood = new Block(17).setHardness(1.5).setResistance(10).setName("Wood");
|
|
||||||
static leaves = new Block(18).setHardness(1.5).setResistance(10).setName("Leaves");
|
|
||||||
static sponge = new Block(19).setHardness(1.5).setResistance(10).setName("Sponge");
|
|
||||||
static glass = new Block(20).setHardness(1.5).setResistance(10).setName("Glass");
|
|
||||||
static lapisOre = new Block(21).setHardness(1.5).setResistance(10).setName("Lapis Ore");
|
|
||||||
static lapisBlock = new Block(22).setHardness(1.5).setResistance(10).setName("Lapis Block");
|
|
||||||
static dispenser = new Block(23).setHardness(1.5).setResistance(10).setName("Dispenser");
|
|
||||||
static sandStone = new Block(24).setHardness(1.5).setResistance(10).setName("Sandstone");
|
|
||||||
static noteBlock = new Block(25).setHardness(1.5).setResistance(10).setName("Noteblock");
|
|
||||||
static blockBed = new Block(26).setHardness(1.5).setResistance(10).setName("Bed");
|
|
||||||
static poweredRail = new Block(27).setHardness(1.5).setResistance(10).setName("Powered Rail");
|
|
||||||
static detectorRail = new Block(28).setHardness(1.5).setResistance(10).setName("Detector Rail");
|
|
||||||
static stickyPisonBase = new Block(29).setHardness(1.5).setResistance(10).setName("Sticky Piston Base");
|
|
||||||
static cobweb = new Block(30).setHardness(4).setName("Cobweb");
|
|
||||||
static tallGrass = new Block(31).setName("Tall Grass");
|
|
||||||
static deadBush = new Block(32).setName("Dead Bush");
|
|
||||||
static pistonBase = new Block(33).setName("Piston Base");
|
|
||||||
static pistonExtension = new Block(34).setName("Piston Extension");
|
|
||||||
static wool = new Block(35).setHardness(0.8).setName("Wool");
|
|
||||||
static pistonMoving = new Block(36).setName("Piston Moving")
|
|
||||||
static dandilion = new Block(37).setName("Dandilion");
|
|
||||||
static rose = new Block(38).setName("Rose");
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Block;
|
|
36
server/Chunk.ts
Normal file
36
server/Chunk.ts
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import { Block } from "./blocks/Block";
|
||||||
|
import { World } from "./World";
|
||||||
|
|
||||||
|
export class Chunk {
|
||||||
|
private readonly MAX_HEIGHT:number = 128;
|
||||||
|
private readonly world:World;
|
||||||
|
private readonly x:number;
|
||||||
|
private readonly z:number;
|
||||||
|
|
||||||
|
private blocks:Uint8Array;
|
||||||
|
|
||||||
|
public static CreateCoordPair(x:number, z:number) {
|
||||||
|
return (x >= 0 ? 0 : 2147483648) | (x & 0x7fff) << 16 | (z >= 0 ? 0 : 0x8000) | z & 0x7fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
public constructor(world:World, x:number, z:number) {
|
||||||
|
this.world = world;
|
||||||
|
this.x = x;
|
||||||
|
this.z = z;
|
||||||
|
this.blocks = new Uint8Array(16 * 16 * this.MAX_HEIGHT);
|
||||||
|
|
||||||
|
this.world.generator.generate(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public setBlock(blockId:number, x:number, y:number, z:number) {
|
||||||
|
this.blocks[x << 11 | z << 7 | y] = blockId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getBlockId(x:number, y:number, z:number) {
|
||||||
|
return this.blocks[x << 11 | z << 7 | y];
|
||||||
|
}
|
||||||
|
|
||||||
|
public getData() {
|
||||||
|
return this.blocks;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,30 +0,0 @@
|
||||||
/*
|
|
||||||
============- Converter.js -============
|
|
||||||
Created by Holly (tgpethan) (c) 2021
|
|
||||||
Licenced under MIT
|
|
||||||
========================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
module.exports.toAbsoluteInt = function(float = 0.0) {
|
|
||||||
return Math.floor(float * 32.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports.to360Fraction = function(float = 0.0) {
|
|
||||||
if (float < 0) {
|
|
||||||
return Math.abs(Math.max(Math.floor((map(float, 0, -360, 360, 0) / 360) * 256) - 1, 0));
|
|
||||||
} else if (float > 0) {
|
|
||||||
return Math.max(Math.floor((float / 360) * 256) - 1, 0);
|
|
||||||
}
|
|
||||||
//return Math.max(Math.floor((float / 360) * 256), 0) - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
function map(input, inputMin, inputMax, outputMin, outputMax) {
|
|
||||||
const newv = (input - inputMin) / (inputMax - inputMin) * (outputMax - outputMin) + outputMin;
|
|
||||||
|
|
||||||
if (outputMin < outputMax) return constrain(newv, outputMin, outputMax);
|
|
||||||
else return constrain(newv, outputMax, outputMin);
|
|
||||||
}
|
|
||||||
|
|
||||||
function constrain(input, low, high) {
|
|
||||||
return Math.max(Math.min(input, high), low);
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
/*
|
|
||||||
==============- Entity.js -=============
|
|
||||||
Created by Holly (tgpethan) (c) 2021
|
|
||||||
Licenced under MIT
|
|
||||||
========================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
class Entity {
|
|
||||||
constructor(EID = 0, x = 0, y = 0, z = 0, yaw = 0, pitch = 0) {
|
|
||||||
this.EID = EID;
|
|
||||||
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
this.z = z;
|
|
||||||
this.yaw = yaw;
|
|
||||||
this.pitch = pitch;
|
|
||||||
|
|
||||||
this.motionX = 0;
|
|
||||||
this.motionY = 0;
|
|
||||||
this.motionZ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
onTick() {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Entity;
|
|
|
@ -1,20 +0,0 @@
|
||||||
/*
|
|
||||||
============- EntityItem.js -===========
|
|
||||||
Created by Holly (tgpethan) (c) 2021
|
|
||||||
Licenced under MIT
|
|
||||||
========================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
const Entity = require("./Entity.js");
|
|
||||||
|
|
||||||
class EntityItem extends Entity {
|
|
||||||
constructor(itemStack, x = 0, y = 0, z = 0) {
|
|
||||||
super(global.fromIDPool(), x, y, z);
|
|
||||||
|
|
||||||
this.itemStack = itemStack;
|
|
||||||
|
|
||||||
this.motionX = (Math.random() * 0.2 - 0.1);
|
|
||||||
this.motionY = 0.2;
|
|
||||||
this.motionZ = (Math.random() * 0.2 - 0.1);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
/*
|
|
||||||
==========- EntityLiving.js -===========
|
|
||||||
Created by Holly (tgpethan) (c) 2021
|
|
||||||
Licenced under MIT
|
|
||||||
========================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
const Entity = require("./Entity.js");
|
|
||||||
|
|
||||||
class EntityLiving extends Entity {
|
|
||||||
constructor(EID = 0, x = 0, y = 0, z = 0) {
|
|
||||||
super(EID, x, y, z);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
onTick() {
|
|
||||||
super.onTick();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = EntityLiving;
|
|
|
@ -1,73 +0,0 @@
|
||||||
/*
|
|
||||||
===========- EntityPlayer.js -==========
|
|
||||||
Created by Holly (tgpethan) (c) 2021
|
|
||||||
Licenced under MIT
|
|
||||||
========================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
const EntityLiving = require("./EntityLiving.js");
|
|
||||||
const user = require("../user.js");
|
|
||||||
|
|
||||||
const Converter = require("../Converter.js");
|
|
||||||
const PacketMappingTable = require("../PacketMappingTable.js");
|
|
||||||
const NamedPackets = require("../NamedPackets.js");
|
|
||||||
|
|
||||||
class EntityPlayer extends EntityLiving {
|
|
||||||
constructor(parent = new user, x = 0, y = 0, z = 0) {
|
|
||||||
super(parent.id, x, y, z);
|
|
||||||
|
|
||||||
this.lastX = 0;
|
|
||||||
this.lastY = 0;
|
|
||||||
this.lastZ = 0;
|
|
||||||
this.absX = 0;
|
|
||||||
this.absY = 0;
|
|
||||||
this.absZ = 0;
|
|
||||||
|
|
||||||
this.absYaw = 0;
|
|
||||||
this.absPitch = 0;
|
|
||||||
|
|
||||||
this.lastYaw = 0;
|
|
||||||
this.lastPitch = 0;
|
|
||||||
|
|
||||||
this.allPacket = new PacketMappingTable[NamedPackets.EntityTeleport](this.EID, this.x, this.y, this.z, this.absYaw, this.absPitch);
|
|
||||||
this.allPacket.soup();
|
|
||||||
this.allPacket.writePacket();
|
|
||||||
|
|
||||||
this.parentPlayer = parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
onTick() {
|
|
||||||
super.onTick();
|
|
||||||
|
|
||||||
this.absX = this.x.toFixed(2);
|
|
||||||
this.absY = this.y.toFixed(2);
|
|
||||||
this.absZ = this.z.toFixed(2);
|
|
||||||
|
|
||||||
this.absYaw = Math.floor(this.yaw);
|
|
||||||
this.absPitch = Math.floor(this.pitch);
|
|
||||||
|
|
||||||
if ((this.absX != this.lastX || this.absY != this.lastY || this.absZ != this.lastZ)) {
|
|
||||||
// all
|
|
||||||
this.allPacket.writer.offset = 5;
|
|
||||||
this.allPacket.writer.writeInt(Converter.toAbsoluteInt(this.x));
|
|
||||||
this.allPacket.writer.writeInt(Converter.toAbsoluteInt(this.y));
|
|
||||||
this.allPacket.writer.writeInt(Converter.toAbsoluteInt(this.z));
|
|
||||||
this.allPacket.writer.writeUByte(Converter.to360Fraction(this.absYaw));
|
|
||||||
this.allPacket.writer.writeUByte(Converter.to360Fraction(this.absPitch));
|
|
||||||
|
|
||||||
global.sendToAllPlayersButSelf(this.EID, this.allPacket.toBuffer());
|
|
||||||
} else if (this.absYaw != this.lastYaw || this.absPitch != this.lastPitch) {
|
|
||||||
// look only
|
|
||||||
global.sendToAllPlayersButSelf(this.EID, new PacketMappingTable[NamedPackets.EntityLook](this.EID, this.absYaw, this.absPitch).writePacket());
|
|
||||||
}
|
|
||||||
|
|
||||||
this.lastYaw = this.absYaw;
|
|
||||||
this.lastPitch = this.absPitch;
|
|
||||||
|
|
||||||
this.lastX = this.absX;
|
|
||||||
this.lastY = this.absY;
|
|
||||||
this.lastZ = this.absZ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = EntityPlayer;
|
|
|
@ -1,31 +0,0 @@
|
||||||
const Block = require("../Blocks/Block.js");
|
|
||||||
|
|
||||||
module.exports = function(x = 0, z = 0) {
|
|
||||||
// Create chunk
|
|
||||||
let chunk = {};
|
|
||||||
for (let y = 0; y < 128; y++) {
|
|
||||||
chunk[y] = {};
|
|
||||||
for (let x = 0; x < 16; x++) {
|
|
||||||
chunk[y][x] = {};
|
|
||||||
for (let z = 0; z < 16; z++) {
|
|
||||||
chunk[y][x][z] = [0, 0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let y = 0; y < 128; y++) {
|
|
||||||
for (let x = 0; x < 16; x++) {
|
|
||||||
for (let z = 0; z < 16; z++) {
|
|
||||||
if (y == 64) {
|
|
||||||
chunk[y][x][z] = 2;
|
|
||||||
}
|
|
||||||
else if (y == 63 || y == 62) chunk[y][x][z][0] = Block.dirt.blockID;
|
|
||||||
else if (y == 0) chunk[y][x][z][0] = Block.bedrock.blockID;
|
|
||||||
else if (y < 62) chunk[y][x][z][0] = Block.stone.blockID;
|
|
||||||
else chunk[y][x][z][0] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return chunk;
|
|
||||||
}
|
|
|
@ -1,187 +0,0 @@
|
||||||
const { perlin2D } = require("./perlin.js");
|
|
||||||
const Block = require("../Blocks/Block.js");
|
|
||||||
|
|
||||||
module.exports = function(cx = 0, cz = 0, seed = 0) {
|
|
||||||
// Create bare chunk
|
|
||||||
let chunk = {};
|
|
||||||
for (let y = 0; y < 128; y++) {
|
|
||||||
chunk[y] = {};
|
|
||||||
for (let x = 0; x < 16; x++) {
|
|
||||||
chunk[y][x] = {};
|
|
||||||
for (let z = 0; z < 16; z++) {
|
|
||||||
chunk[y][x][z] = [0, 0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let stripTopCoord = {};
|
|
||||||
// History has shown it's better to alloc all at once
|
|
||||||
for (let x = 0; x < 16; x++) {
|
|
||||||
stripTopCoord[x] = {};
|
|
||||||
for (let z = 0; z < 16; z++) {
|
|
||||||
stripTopCoord[x][z] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate top layer of grass
|
|
||||||
for (let x = 0; x < 16; x++) {
|
|
||||||
for (let z = 0; z < 16; z++) {
|
|
||||||
// NOTE: Because of the way this is, it is not random at all. The heightmap is simply offset so uhhh.
|
|
||||||
// TODO: Figure out a better way of dealing with this :)
|
|
||||||
const layer1 = (64 + (perlin2D(((cx << 4) + x) / 15, ((cz << 4) + z) / 15) * 10));
|
|
||||||
const layer2 = (64 + (perlin2D(((cx + (10 + seed) << 4) + x) / 15, ((cz + (4 + seed) << 4) + z) / 15) * 10));
|
|
||||||
const layer3_1 = (64 + (perlin2D(((cx + (-15 + seed) << 4) + x) / 15, ((cz + (-2 + seed) << 4) + z) / 15) * 23));
|
|
||||||
const layer3_2 = (64 + (perlin2D(((cx + (25 + seed) << 4) + x) / 15, ((cz + (-17 + seed) << 4) + z) / 15) * 40));
|
|
||||||
const layer3 = (layer3_1 + layer3_2) / 2;
|
|
||||||
const average = Math.floor((layer1 + layer2 + layer3) / 3);
|
|
||||||
stripTopCoord[x][z] = average;
|
|
||||||
chunk[average][x][z][0] = Block.grass.blockID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate down from the top layer
|
|
||||||
for (let x = 0; x < 16; x++) {
|
|
||||||
for (let z = 0; z < 16; z++) {
|
|
||||||
// Cache these otherwise we'll be doing more maths 128 times for each column and row
|
|
||||||
const topM1 = stripTopCoord[x][z] - 1,
|
|
||||||
topM2 = topM1 - 1;
|
|
||||||
|
|
||||||
for (let y = stripTopCoord[x][z]; y != -1; y--) {
|
|
||||||
if (y == topM1 || y == topM2) chunk[y][x][z][0] = Block.dirt.blockID;
|
|
||||||
else if (y == 0) chunk[y][x][z][0] = Block.bedrock.blockID;
|
|
||||||
else if (y < topM2) chunk[y][x][z][0] = Block.stone.blockID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2nd pass
|
|
||||||
for (let y = 0; y < 128; y++) {
|
|
||||||
for (let x = 0; x < 16; x++) {
|
|
||||||
for (let z = 0; z < 16; z++) {
|
|
||||||
if (chunk[y][x][z][0] == 0 && y < 64) chunk[y][x][z][0] = Block.waterStill.blockID;
|
|
||||||
|
|
||||||
if (y < 127 && y > 0) if (chunk[y][x][z][0] == Block.waterStill.blockID && chunk[y - 1][x][z][0] == 2) chunk[y - 1][x][z][0] = Block.dirt.blockID;
|
|
||||||
|
|
||||||
//if (x == 0 && z == 0) chunk[y][x][z] = 57;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let treeBlocks = [];
|
|
||||||
|
|
||||||
const chunkX = cx << 4;
|
|
||||||
const chunkZ = cz << 4;
|
|
||||||
let topBlock = 0;
|
|
||||||
// 3rd pass???
|
|
||||||
for (let x = 0; x < 16; x++) {
|
|
||||||
for (let z = 0; z < 16; z++) {
|
|
||||||
topBlock = stripTopCoord[x][z];
|
|
||||||
|
|
||||||
if (chunk[topBlock][x][z][0] == Block.grass.blockID) {
|
|
||||||
if (Math.floor(Math.random() * 5) == 0) {
|
|
||||||
chunk[topBlock + 1][x][z][0] = Block.tallGrass.blockID;
|
|
||||||
chunk[topBlock + 1][x][z][1] = 1;
|
|
||||||
} else if (Math.floor(Math.random() * 150) == 0) {
|
|
||||||
chunk[topBlock + 1][x][z][0] = Block.rose.blockID;
|
|
||||||
} else if (Math.floor(Math.random() * 150) == 0) {
|
|
||||||
chunk[topBlock + 1][x][z][0] = Block.dandilion.blockID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Need a better way of doing this it currently takes a severely long time (gee I wonder why)
|
|
||||||
if (chunk[topBlock][x][z][0] == 2 && Math.floor(Math.random() * 200) == 0) {
|
|
||||||
chunk[topBlock][x][z][0] = 3;
|
|
||||||
// Logs
|
|
||||||
treeBlocks.push([(chunkX + x), topBlock + 1, (chunkZ + z), Block.wood.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x), topBlock + 2, (chunkZ + z), Block.wood.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x), topBlock + 3, (chunkZ + z), Block.wood.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x), topBlock + 4, (chunkZ + z), Block.wood.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x), topBlock + 5, (chunkZ + z), Block.wood.blockID]);
|
|
||||||
|
|
||||||
// Leaves
|
|
||||||
// Layer 1
|
|
||||||
treeBlocks.push([(chunkX + x) - 1, topBlock + 3, (chunkZ + z), Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x) - 2, topBlock + 3, (chunkZ + z), Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x) + 1, topBlock + 3, (chunkZ + z), Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x) + 2, topBlock + 3, (chunkZ + z), Block.leaves.blockID]);
|
|
||||||
|
|
||||||
treeBlocks.push([(chunkX + x), topBlock + 3, (chunkZ + z) - 1, Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x), topBlock + 3, (chunkZ + z) - 2, Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x), topBlock + 3, (chunkZ + z) + 1, Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x), topBlock + 3, (chunkZ + z) + 2, Block.leaves.blockID]);
|
|
||||||
|
|
||||||
treeBlocks.push([(chunkX + x) + 1, topBlock + 3, (chunkZ + z) - 1, Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x) + 1, topBlock + 3, (chunkZ + z) - 2, Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x) + 1, topBlock + 3, (chunkZ + z) + 1, Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x) + 1, topBlock + 3, (chunkZ + z) + 2, Block.leaves.blockID]);
|
|
||||||
|
|
||||||
treeBlocks.push([(chunkX + x) + 2, topBlock + 3, (chunkZ + z) - 1, Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x) + 2, topBlock + 3, (chunkZ + z) - 2, Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x) + 2, topBlock + 3, (chunkZ + z) + 1, Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x) + 2, topBlock + 3, (chunkZ + z) + 2, Block.leaves.blockID]);
|
|
||||||
|
|
||||||
treeBlocks.push([(chunkX + x) - 1, topBlock + 3, (chunkZ + z) - 1, Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x) - 1, topBlock + 3, (chunkZ + z) - 2, Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x) - 1, topBlock + 3, (chunkZ + z) + 1, Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x) - 1, topBlock + 3, (chunkZ + z) + 2, Block.leaves.blockID]);
|
|
||||||
|
|
||||||
treeBlocks.push([(chunkX + x) - 2, topBlock + 3, (chunkZ + z) - 1, Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x) - 2, topBlock + 3, (chunkZ + z) - 2, Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x) - 2, topBlock + 3, (chunkZ + z) + 1, Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x) - 2, topBlock + 3, (chunkZ + z) + 2, Block.leaves.blockID]);
|
|
||||||
|
|
||||||
// Layer 2
|
|
||||||
treeBlocks.push([(chunkX + x) - 1, topBlock + 4, (chunkZ + z), Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x) - 2, topBlock + 4, (chunkZ + z), Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x) + 1, topBlock + 4, (chunkZ + z), Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x) + 2, topBlock + 4, (chunkZ + z), Block.leaves.blockID]);
|
|
||||||
|
|
||||||
treeBlocks.push([(chunkX + x), topBlock + 4, (chunkZ + z) - 1, Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x), topBlock + 4, (chunkZ + z) - 2, Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x), topBlock + 4, (chunkZ + z) + 1, Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x), topBlock + 4, (chunkZ + z) + 2, Block.leaves.blockID]);
|
|
||||||
|
|
||||||
treeBlocks.push([(chunkX + x) + 1, topBlock + 4, (chunkZ + z) - 1, Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x) + 1, topBlock + 4, (chunkZ + z) - 2, Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x) + 1, topBlock + 4, (chunkZ + z) + 1, Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x) + 1, topBlock + 4, (chunkZ + z) + 2, Block.leaves.blockID]);
|
|
||||||
|
|
||||||
treeBlocks.push([(chunkX + x) + 2, topBlock + 4, (chunkZ + z) - 1, Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x) + 2, topBlock + 4, (chunkZ + z) - 2, Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x) + 2, topBlock + 4, (chunkZ + z) + 1, Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x) + 2, topBlock + 4, (chunkZ + z) + 2, Block.leaves.blockID]);
|
|
||||||
|
|
||||||
treeBlocks.push([(chunkX + x) - 1, topBlock + 4, (chunkZ + z) - 1, Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x) - 1, topBlock + 4, (chunkZ + z) - 2, Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x) - 1, topBlock + 4, (chunkZ + z) + 1, Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x) - 1, topBlock + 4, (chunkZ + z) + 2, Block.leaves.blockID]);
|
|
||||||
|
|
||||||
treeBlocks.push([(chunkX + x) - 2, topBlock + 4, (chunkZ + z) - 1, Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x) - 2, topBlock + 4, (chunkZ + z) - 2, Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x) - 2, topBlock + 4, (chunkZ + z) + 1, Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x) - 2, topBlock + 4, (chunkZ + z) + 2, Block.leaves.blockID]);
|
|
||||||
|
|
||||||
// Layer 3
|
|
||||||
treeBlocks.push([(chunkX + x) - 1, topBlock + 5, (chunkZ + z), Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x) + 1, topBlock + 5, (chunkZ + z), Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x), topBlock + 5, (chunkZ + z) - 1, Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x), topBlock + 5, (chunkZ + z) + 1, Block.leaves.blockID]);
|
|
||||||
|
|
||||||
treeBlocks.push([(chunkX + x) - 1, topBlock + 5, (chunkZ + z) - 1, Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x) + 1, topBlock + 5, (chunkZ + z) + 1, Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x) - 1, topBlock + 5, (chunkZ + z) - 1, Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x) + 1, topBlock + 5, (chunkZ + z) + 1, Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x) + 1, topBlock + 5, (chunkZ + z) - 1, Block.leaves.blockID]);
|
|
||||||
|
|
||||||
// Layer 4
|
|
||||||
treeBlocks.push([(chunkX + x) - 1, topBlock + 6, (chunkZ + z), Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x) + 1, topBlock + 6, (chunkZ + z), Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x), topBlock + 6, (chunkZ + z) - 1, Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x), topBlock + 6, (chunkZ + z) + 1, Block.leaves.blockID]);
|
|
||||||
treeBlocks.push([(chunkX + x), topBlock + 6, (chunkZ + z), Block.leaves.blockID]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [chunk, treeBlocks];
|
|
||||||
}
|
|
|
@ -1,307 +0,0 @@
|
||||||
/**
|
|
||||||
* A Perlin Noise library for JavaScript.
|
|
||||||
*
|
|
||||||
* Based on implementations by Ken Perlin (Java, C) & Stefan Gustavson (C).
|
|
||||||
*
|
|
||||||
* MIT License.
|
|
||||||
* Copyright (c) 2018 Leonardo de S.L.F.
|
|
||||||
* http://leodeslf.com/
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Permutation table.
|
|
||||||
|
|
||||||
const p = [
|
|
||||||
151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140,
|
|
||||||
36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234,
|
|
||||||
75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237,
|
|
||||||
149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48,
|
|
||||||
27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105,
|
|
||||||
92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73,
|
|
||||||
209, 76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86,
|
|
||||||
164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38,
|
|
||||||
147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189,
|
|
||||||
28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153,
|
|
||||||
101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224,
|
|
||||||
232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144,
|
|
||||||
12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214,
|
|
||||||
31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150,
|
|
||||||
254, 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66,
|
|
||||||
215, 61, 156, 180
|
|
||||||
];
|
|
||||||
const P = [...p, ...p];
|
|
||||||
|
|
||||||
// Utility functions.
|
|
||||||
|
|
||||||
function fade(t) {
|
|
||||||
return t * t * t * (t * (t * 6 - 15) + 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
function lerp(t, a, b) {
|
|
||||||
return a + t * (b - a);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper functions to find gradients for each vertex.
|
|
||||||
|
|
||||||
function gradient1D(hash, x) {
|
|
||||||
return hash & 1 ? -x : x;
|
|
||||||
}
|
|
||||||
|
|
||||||
function gradient2D(hash, x, y) {
|
|
||||||
const H = hash & 3;
|
|
||||||
return (H < 2 ? x : -x) + (H === 0 || H === 2 ? y : -y);
|
|
||||||
}
|
|
||||||
|
|
||||||
function gradient3D(hash, x, y, z) {
|
|
||||||
const
|
|
||||||
H = hash & 15,
|
|
||||||
U = H < 8 ? x : y,
|
|
||||||
V = H < 4 ? y : H === 12 || H === 14 ? x : z;
|
|
||||||
return ((H & 1) ? -U : U) + ((H & 2) ? -V : V);
|
|
||||||
}
|
|
||||||
|
|
||||||
function gradient4D(hash, x, y, z, t) {
|
|
||||||
const
|
|
||||||
H = hash & 31,
|
|
||||||
U = H < 24 ? x : y,
|
|
||||||
V = H < 16 ? y : z,
|
|
||||||
W = H < 8 ? z : t;
|
|
||||||
return ((H & 1) ? -U : U) + ((H & 2) ? -V : V) + ((H & 4) ? -W : W);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perlin Noise functions.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a one-dimensional noise value between -1 and 1.
|
|
||||||
* @param {number} x A numeric expression.
|
|
||||||
* @returns {number} Perlin Noise value.
|
|
||||||
*/
|
|
||||||
function perlin1D(x) {
|
|
||||||
const
|
|
||||||
FX = Math.floor(x),
|
|
||||||
X = FX & 255;
|
|
||||||
|
|
||||||
x = x - FX;
|
|
||||||
|
|
||||||
return lerp(
|
|
||||||
fade(x),
|
|
||||||
gradient1D(P[P[X]], x),
|
|
||||||
gradient1D(P[P[X + 1]], x - 1)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a two-dimensional noise value between -1 and 1.
|
|
||||||
* @param {number} x A numeric expression.
|
|
||||||
* @param {number} y A numeric expression.
|
|
||||||
* @returns {number} Perlin Noise value.
|
|
||||||
*/
|
|
||||||
function perlin2D(x = 0, y = 0) {
|
|
||||||
// Hack to allow this to work with integer block coords
|
|
||||||
x += 0.2;
|
|
||||||
y += 0.2;
|
|
||||||
|
|
||||||
const FX = Math.floor(x),
|
|
||||||
FY = Math.floor(y),
|
|
||||||
X = FX & 255,
|
|
||||||
Y = FY & 255,
|
|
||||||
A = P[X] + Y,
|
|
||||||
B = P[X + 1] + Y;
|
|
||||||
|
|
||||||
x = x - FX;
|
|
||||||
y = y - FY;
|
|
||||||
|
|
||||||
const
|
|
||||||
FDX = fade(x),
|
|
||||||
x1 = x - 1,
|
|
||||||
y1 = y - 1;
|
|
||||||
|
|
||||||
return lerp(
|
|
||||||
fade(y),
|
|
||||||
lerp(
|
|
||||||
FDX,
|
|
||||||
gradient2D(P[A], x, y),
|
|
||||||
gradient2D(P[B], x1, y)
|
|
||||||
),
|
|
||||||
lerp(
|
|
||||||
FDX,
|
|
||||||
gradient2D(P[A + 1], x, y1),
|
|
||||||
gradient2D(P[B + 1], x1, y1)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a three-dimensional noise value between -1 and 1.
|
|
||||||
* @param {number} x A numeric expression.
|
|
||||||
* @param {number} y A numeric expression.
|
|
||||||
* @param {number} z A numeric expression.
|
|
||||||
* @returns {number} Perlin Noise value.
|
|
||||||
*/
|
|
||||||
function perlin3D(x, y, z) {
|
|
||||||
const
|
|
||||||
FX = Math.floor(x),
|
|
||||||
FY = Math.floor(y),
|
|
||||||
FZ = Math.floor(z),
|
|
||||||
X = FX & 255,
|
|
||||||
Y = FY & 255,
|
|
||||||
Z = FZ & 255,
|
|
||||||
A = P[X] + Y,
|
|
||||||
B = P[X + 1] + Y,
|
|
||||||
AA = P[A] + Z,
|
|
||||||
BA = P[B] + Z,
|
|
||||||
AB = P[A + 1] + Z,
|
|
||||||
BB = P[B + 1] + Z;
|
|
||||||
|
|
||||||
x = x - FX;
|
|
||||||
y = y - FY;
|
|
||||||
z = z - FZ;
|
|
||||||
|
|
||||||
const
|
|
||||||
FDX = fade(x),
|
|
||||||
FDY = fade(y),
|
|
||||||
x1 = x - 1,
|
|
||||||
y1 = y - 1,
|
|
||||||
z1 = z - 1;
|
|
||||||
|
|
||||||
return lerp(
|
|
||||||
fade(z),
|
|
||||||
lerp(
|
|
||||||
FDY,
|
|
||||||
lerp(
|
|
||||||
FDX,
|
|
||||||
gradient3D(P[AA], x, y, z),
|
|
||||||
gradient3D(P[BA], x1, y, z)
|
|
||||||
),
|
|
||||||
lerp(
|
|
||||||
FDX,
|
|
||||||
gradient3D(P[AB], x, y1, z),
|
|
||||||
gradient3D(P[BB], x1, y1, z)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
lerp(
|
|
||||||
FDY,
|
|
||||||
lerp(
|
|
||||||
FDX,
|
|
||||||
gradient3D(P[AA + 1], x, y, z1),
|
|
||||||
gradient3D(P[BA + 1], x1, y, z1)
|
|
||||||
),
|
|
||||||
lerp(
|
|
||||||
FDX,
|
|
||||||
gradient3D(P[AB + 1], x, y1, z1),
|
|
||||||
gradient3D(P[BB + 1], x1, y1, z1)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a four-dimensional noise value between -1 and 1.
|
|
||||||
* @param {number} x A numeric expression.
|
|
||||||
* @param {number} y A numeric expression.
|
|
||||||
* @param {number} z A numeric expression.
|
|
||||||
* @param {number} t A numeric expression.
|
|
||||||
* @returns {number} Perlin Noise value.
|
|
||||||
*/
|
|
||||||
function perlin4D(x, y, z, t) {
|
|
||||||
const
|
|
||||||
FX = Math.floor(x),
|
|
||||||
FY = Math.floor(y),
|
|
||||||
FZ = Math.floor(z),
|
|
||||||
FT = Math.floor(t),
|
|
||||||
X = FX & 255,
|
|
||||||
Y = FY & 255,
|
|
||||||
Z = FZ & 255,
|
|
||||||
T = FT & 255,
|
|
||||||
A = P[X] + Y,
|
|
||||||
B = P[X + 1] + Y,
|
|
||||||
AA = P[A] + Z,
|
|
||||||
BA = P[B] + Z,
|
|
||||||
AB = P[A + 1] + Z,
|
|
||||||
BB = P[B + 1] + Z,
|
|
||||||
AAA = P[AA] + T,
|
|
||||||
BAA = P[BA] + T,
|
|
||||||
ABA = P[AB] + T,
|
|
||||||
BBA = P[BB] + T,
|
|
||||||
AAB = P[AA + 1] + T,
|
|
||||||
BAB = P[BA + 1] + T,
|
|
||||||
ABB = P[AB + 1] + T,
|
|
||||||
BBB = P[BB + 1] + T;
|
|
||||||
|
|
||||||
x = x - FX;
|
|
||||||
y = y - FY;
|
|
||||||
z = z - FZ;
|
|
||||||
t = t - FT;
|
|
||||||
|
|
||||||
const
|
|
||||||
FDX = fade(x),
|
|
||||||
FDY = fade(y),
|
|
||||||
FDZ = fade(z),
|
|
||||||
x1 = x - 1,
|
|
||||||
y1 = y - 1,
|
|
||||||
z1 = z - 1,
|
|
||||||
t1 = t - 1;
|
|
||||||
|
|
||||||
return lerp(
|
|
||||||
fade(t),
|
|
||||||
lerp(
|
|
||||||
FDZ,
|
|
||||||
lerp(
|
|
||||||
FDY,
|
|
||||||
lerp(
|
|
||||||
FDX,
|
|
||||||
gradient4D(P[AAA], x, y, z, t),
|
|
||||||
gradient4D(P[BAA], x1, y, z, t)
|
|
||||||
),
|
|
||||||
lerp(
|
|
||||||
FDX,
|
|
||||||
gradient4D(P[ABA], x, y1, z, t),
|
|
||||||
gradient4D(P[BBA], x1, y1, z, t)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
lerp(
|
|
||||||
FDY,
|
|
||||||
lerp(
|
|
||||||
FDX,
|
|
||||||
gradient4D(P[AAB], x, y, z1, t),
|
|
||||||
gradient4D(P[BAB], x1, y, z1, t)
|
|
||||||
),
|
|
||||||
lerp(
|
|
||||||
FDX,
|
|
||||||
gradient4D(P[ABB], x, y1, z1, t),
|
|
||||||
gradient4D(P[BBB], x1, y1, z1, t)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
lerp(
|
|
||||||
FDZ,
|
|
||||||
lerp(
|
|
||||||
FDY,
|
|
||||||
lerp(
|
|
||||||
FDX,
|
|
||||||
gradient4D(P[AAA + 1], x, y, z, t1),
|
|
||||||
gradient4D(P[BAA + 1], x1, y, z, t1)
|
|
||||||
),
|
|
||||||
lerp(
|
|
||||||
FDX,
|
|
||||||
gradient4D(P[ABA + 1], x, y1, z, t1),
|
|
||||||
gradient4D(P[BBA + 1], x1, y1, z, t1)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
lerp(
|
|
||||||
FDY,
|
|
||||||
lerp(
|
|
||||||
FDX,
|
|
||||||
gradient4D(P[AAB + 1], x, y, z1, t1),
|
|
||||||
gradient4D(P[BAB + 1], x1, y, z1, t1)
|
|
||||||
),
|
|
||||||
lerp(
|
|
||||||
FDX,
|
|
||||||
gradient4D(P[ABB + 1], x, y1, z1, t1),
|
|
||||||
gradient4D(P[BBB + 1], x1, y1, z1, t1)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = { perlin1D, perlin2D, perlin3D, perlin4D };
|
|
|
@ -1,23 +0,0 @@
|
||||||
const ItemStack = require("../ItemStack.js");
|
|
||||||
|
|
||||||
module.exports = class {
|
|
||||||
constructor() {
|
|
||||||
this.Slots = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
saveData() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
loadData() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
getSlot(slot = 0) {
|
|
||||||
return this.Slots[slot]; // If the slot doesn't exist well sucks to be you I guess! Haha :Þ
|
|
||||||
}
|
|
||||||
|
|
||||||
setSlot(slot = 0, itemStack = new ItemStack) {
|
|
||||||
this.Slots[slot] = itemStack;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
module.exports = class {
|
|
||||||
constructor(id, count) {
|
|
||||||
this.id = id;
|
|
||||||
this.count = count;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateCount(count) {
|
|
||||||
this.count += count;
|
|
||||||
}
|
|
||||||
}
|
|
21
server/MPClient.ts
Normal file
21
server/MPClient.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import { Socket } from "net";
|
||||||
|
import { IEntity } from "./entities/IEntity";
|
||||||
|
import { Writer } from "../bufferStuff";
|
||||||
|
|
||||||
|
export class MPClient {
|
||||||
|
private readonly socket:Socket;
|
||||||
|
private readonly entity:IEntity;
|
||||||
|
|
||||||
|
public constructor(socket:Socket, entity:IEntity) {
|
||||||
|
this.socket = socket;
|
||||||
|
this.entity = entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
send(buffer:Buffer|Writer) {
|
||||||
|
if (buffer instanceof Writer) {
|
||||||
|
this.socket.write(buffer.toBuffer());
|
||||||
|
} else {
|
||||||
|
this.socket.write(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
124
server/MinecraftServer.ts
Normal file
124
server/MinecraftServer.ts
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
import { Config } from "../config";
|
||||||
|
import { Console } from "../console";
|
||||||
|
import { Server, Socket } from "net";
|
||||||
|
import { FunkyArray } from "../funkyArray";
|
||||||
|
import { World } from "./World";
|
||||||
|
import { Reader } from "../bufferStuff";
|
||||||
|
import { Packets } from "./enums/Packets";
|
||||||
|
import { PacketHandshake } from "./packets/Handshake";
|
||||||
|
import { MPClient } from "./MPClient";
|
||||||
|
import { PacketKeepAlive } from "./packets/KeepAlive";
|
||||||
|
import { PacketLoginRequest } from "./packets/LoginRequest";
|
||||||
|
import { PacketDisconnectKick } from "./packets/DisconnectKick";
|
||||||
|
import { Player } from "./entities/Player";
|
||||||
|
import { PacketTimeUpdate } from "./packets/TimeUpdate";
|
||||||
|
import { PacketSpawnPosition } from "./packets/SpawnPosition";
|
||||||
|
import { Chunk } from "./Chunk";
|
||||||
|
import { PacketMapChunk } from "./packets/MapChunk";
|
||||||
|
import { PacketPlayerPositionLook } from "./packets/PlayerPositionLook";
|
||||||
|
import { PacketPreChunk } from "./packets/PreChunk";
|
||||||
|
|
||||||
|
export class MinecraftServer {
|
||||||
|
private static readonly PROTOCOL_VERSION = 14;
|
||||||
|
private static readonly TICK_RATE = 20;
|
||||||
|
private static readonly TICK_RATE_MS = 1000 / MinecraftServer.TICK_RATE;
|
||||||
|
private readonly keepalivePacket = new PacketKeepAlive().writeData();
|
||||||
|
|
||||||
|
private totalClients:number = 0;
|
||||||
|
private config:Config;
|
||||||
|
private server:Server;
|
||||||
|
private serverClock:NodeJS.Timer;
|
||||||
|
private tickCounter:number = 0;
|
||||||
|
private clients:FunkyArray<number, MPClient>;
|
||||||
|
private worlds:FunkyArray<number, World>;
|
||||||
|
|
||||||
|
public constructor(config:Config) {
|
||||||
|
this.config = config;
|
||||||
|
|
||||||
|
this.clients = new FunkyArray<number, MPClient>();
|
||||||
|
|
||||||
|
this.worlds = new FunkyArray<number, World>();
|
||||||
|
this.worlds.set(0, new World());
|
||||||
|
|
||||||
|
this.serverClock = setInterval(() => {
|
||||||
|
// Every 1 sec
|
||||||
|
if (this.tickCounter % MinecraftServer.TICK_RATE === 0) {
|
||||||
|
if (this.clients.length !== 0) {
|
||||||
|
const timePacket = new PacketTimeUpdate(this.tickCounter).writeData();
|
||||||
|
this.clients.forEach(client => {
|
||||||
|
client.send(this.keepalivePacket);
|
||||||
|
client.send(timePacket);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.worlds.forEach(world => {
|
||||||
|
world.tick(this.tickCounter);
|
||||||
|
});
|
||||||
|
this.tickCounter++;
|
||||||
|
}, MinecraftServer.TICK_RATE_MS);
|
||||||
|
|
||||||
|
this.server = new Server();
|
||||||
|
this.server.on("connection", this.onConnection.bind(this));
|
||||||
|
this.server.listen(config.port, () => Console.printInfo(`Minecraft server started at ${config.port}`));
|
||||||
|
}
|
||||||
|
|
||||||
|
sendToAllClients(buffer:Buffer) {
|
||||||
|
this.clients.forEach(client => {
|
||||||
|
client.send(buffer);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onConnection(socket:Socket) {
|
||||||
|
socket.on("data", chunk => {
|
||||||
|
const reader = new Reader(chunk);
|
||||||
|
|
||||||
|
const packetId = reader.readUByte();
|
||||||
|
//console.log(packetId);
|
||||||
|
switch (packetId) {
|
||||||
|
// Handle timeouts at some point, idk.
|
||||||
|
case Packets.KeepAlive:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Packets.LoginRequest:
|
||||||
|
const loginPacket = new PacketLoginRequest().readData(reader);
|
||||||
|
if (loginPacket.protocolVersion !== MinecraftServer.PROTOCOL_VERSION) {
|
||||||
|
if (loginPacket.protocolVersion > MinecraftServer.PROTOCOL_VERSION) {
|
||||||
|
socket.write(new PacketDisconnectKick("Outdated server!").writeData());
|
||||||
|
} else {
|
||||||
|
socket.write(new PacketDisconnectKick("Outdated or modded client!").writeData());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const world = this.worlds.get(0);
|
||||||
|
if (world instanceof World) {
|
||||||
|
const clientEntity = new Player(this, world, loginPacket.username);
|
||||||
|
world.addEntity(clientEntity);
|
||||||
|
socket.write(new PacketLoginRequest(clientEntity.entityId, "", 0, -1).writeData());
|
||||||
|
socket.write(new PacketSpawnPosition(8, 64, 8).writeData());
|
||||||
|
|
||||||
|
socket.write(new PacketPreChunk(0, 0, true).writeData());
|
||||||
|
const chunk = world.getChunk(0, 0);
|
||||||
|
if (chunk instanceof Chunk) {
|
||||||
|
(async () => {
|
||||||
|
const chunkData = await (new PacketMapChunk(0, 0, 0, 15, 127, 15, chunk).writeData());
|
||||||
|
socket.write(chunkData);
|
||||||
|
socket.write(new PacketPlayerPositionLook(8, 66, 66.62, 8, 0, 0, false).writeData());
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
const client = new MPClient(socket, clientEntity);
|
||||||
|
this.clients.set(this.totalClients++, client);
|
||||||
|
} else {
|
||||||
|
socket.write(new PacketDisconnectKick("Failed to find world to put player in.").writeData());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Packets.Handshake:
|
||||||
|
const handshakePacket = new PacketHandshake().readData(reader);
|
||||||
|
socket.write(handshakePacket.writeData());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,69 +0,0 @@
|
||||||
/*
|
|
||||||
===========- NamedPackets.js -==========
|
|
||||||
Created by Holly (tgpethan) (c) 2021
|
|
||||||
Licenced under MIT
|
|
||||||
========================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
const namedPackets = {
|
|
||||||
"Disconnect": -1,
|
|
||||||
"KeepAlive": 0x00,
|
|
||||||
"LoginRequest": 0x01,
|
|
||||||
"Handshake": 0x02,
|
|
||||||
"ChatMessage": 0x03,
|
|
||||||
"TimeUpdate": 0x04,
|
|
||||||
"EntityEquipment": 0x05,
|
|
||||||
"SpawnPosition": 0x06,
|
|
||||||
"UseEntity": 0x07,
|
|
||||||
"UpdateHealth": 0x08,
|
|
||||||
"Respawn": 0x09,
|
|
||||||
"Player": 0x0A,
|
|
||||||
"PlayerPosition": 0x0B,
|
|
||||||
"PlayerLook": 0x0C,
|
|
||||||
"PlayerPositionAndLook": 0x0D,
|
|
||||||
"PlayerDigging": 0x0E,
|
|
||||||
"PlayerBlockPlacement": 0x0F,
|
|
||||||
"HoldingChange": 0x10,
|
|
||||||
"UseBed": 0x11,
|
|
||||||
"Animation": 0x12,
|
|
||||||
"EntityAction": 0x13,
|
|
||||||
"NamedEntitySpawn": 0x14,
|
|
||||||
"PickupSpawn": 0x15,
|
|
||||||
"CollectItem": 0x16,
|
|
||||||
"AddObjectOrVehicle": 0x17,
|
|
||||||
"MobSpawn": 0x18,
|
|
||||||
"EntityPainting": 0x19,
|
|
||||||
"StanceUpdate": 0x1B,
|
|
||||||
"EntityVelocity": 0x1C,
|
|
||||||
"DestroyEntity": 0x1D,
|
|
||||||
"Entity": 0x1E,
|
|
||||||
"EntityRelativeMove": 0x1F,
|
|
||||||
"EntityLook": 0x20,
|
|
||||||
"EntityLookAndRelativeMove": 0x21,
|
|
||||||
"EntityTeleport": 0x22,
|
|
||||||
"EntityStatus": 0x26,
|
|
||||||
"AttachEntity": 0x27,
|
|
||||||
"EntityMetadata": 0x28,
|
|
||||||
"PreChunk": 0x32,
|
|
||||||
"MapChunk": 0x33,
|
|
||||||
"MultiBlockChange": 0x34,
|
|
||||||
"BlockChange": 0x35,
|
|
||||||
"BlockAction": 0x36,
|
|
||||||
"Explosion": 0x3C,
|
|
||||||
"SoundEffect": 0x3D,
|
|
||||||
"NewOrInvalidState": 0x46,
|
|
||||||
"Thunderbolt": 0x47,
|
|
||||||
"OpenWindow": 0x64,
|
|
||||||
"CloseWindow": 0x65,
|
|
||||||
"WindowClick": 0x66,
|
|
||||||
"SetSlot": 0x67,
|
|
||||||
"WindowItems": 0x68,
|
|
||||||
"UpdateProgressBar": 0x69,
|
|
||||||
"Transaction": 0x6A,
|
|
||||||
"UpdateSign": 0x82,
|
|
||||||
"ItemData": 0x83,
|
|
||||||
"IncrementStatistic": 0xC8,
|
|
||||||
"DisconnectOrKick": 0xFF
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = namedPackets;
|
|
|
@ -1,42 +0,0 @@
|
||||||
/*
|
|
||||||
========- PacketMappingTable.js -=======
|
|
||||||
Created by Holly (tgpethan) (c) 2021
|
|
||||||
Licenced under MIT
|
|
||||||
========================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
const Packet0KeepAlive = require("./Packets/Packet0KeepAlive.js"),
|
|
||||||
Packet1LoginRequest = require("./Packets/Packet1LoginRequest.js"),
|
|
||||||
Packet2Handshake = require("./Packets/Packet2Handshake.js"),
|
|
||||||
Packet3Chat = require("./Packets/Packet3Chat.js"),
|
|
||||||
Packet4TimeUpdate = require("./Packets/Packet4TimeUpdate"),
|
|
||||||
Packet6SpawnPosition = require("./Packets/Packet6SpawnPosition.js"),
|
|
||||||
Packet10Player = require("./Packets/Packet10Player.js"),
|
|
||||||
Packet13PlayerPositionAndLook = require("./Packets/Packet13PlayerPositionAndLook.js"),
|
|
||||||
Packet18Animation = require("./Packets/Packet18Animation.js"),
|
|
||||||
Packet20NamedEntitySpawn = require("./Packets/Packet20NamedEntitySpawn.js"),
|
|
||||||
Packet32EntityLook = require("./Packets/Packet32EntityLook.js"),
|
|
||||||
Packet34EntityTeleport = require("./Packets/Packet34EntityTeleport.js"),
|
|
||||||
Packet50PreChunk = require("./Packets/Packet50PreChunk.js"),
|
|
||||||
Packet53BlockChange = require("./Packets/Packet53BlockChange.js"),
|
|
||||||
Packet103SetSlot = require("./Packets/Packet103SetSlot.js");
|
|
||||||
|
|
||||||
const mappingTable = {
|
|
||||||
0x00: Packet0KeepAlive,
|
|
||||||
0x01: Packet1LoginRequest,
|
|
||||||
0x02: Packet2Handshake,
|
|
||||||
0x03: Packet3Chat,
|
|
||||||
0x04: Packet4TimeUpdate,
|
|
||||||
0x06: Packet6SpawnPosition,
|
|
||||||
0x0A: Packet10Player,
|
|
||||||
0x0D: Packet13PlayerPositionAndLook,
|
|
||||||
0x12: Packet18Animation,
|
|
||||||
0x14: Packet20NamedEntitySpawn,
|
|
||||||
0x20: Packet32EntityLook,
|
|
||||||
0x22: Packet34EntityTeleport,
|
|
||||||
0x32: Packet50PreChunk,
|
|
||||||
0x35: Packet53BlockChange,
|
|
||||||
0x67: Packet103SetSlot
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = mappingTable;
|
|
|
@ -1,30 +0,0 @@
|
||||||
/*
|
|
||||||
==============- Packet.js -=============
|
|
||||||
Created by Holly (tgpethan) (c) 2021
|
|
||||||
Licenced under MIT
|
|
||||||
========================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
const bufferStuff = require("../bufferStuff.js");
|
|
||||||
|
|
||||||
module.exports = class {
|
|
||||||
constructor(packetID = 0x00) {
|
|
||||||
this.id = packetID;
|
|
||||||
|
|
||||||
this.packetSize = 0;
|
|
||||||
|
|
||||||
this.writer = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
writePacket() {
|
|
||||||
this.writer = new bufferStuff.Writer(this.packetSize);
|
|
||||||
|
|
||||||
this.writer.writeByte(this.id);
|
|
||||||
|
|
||||||
return this.writer;
|
|
||||||
}
|
|
||||||
|
|
||||||
toBuffer() {
|
|
||||||
return this.writer == null ? Buffer.alloc(0) : this.writer.buffer;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
/*
|
|
||||||
========- Packet0KeepAlive.js -=========
|
|
||||||
Created by Holly (tgpethan) (c) 2021
|
|
||||||
Licenced under MIT
|
|
||||||
========================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
const Packet = require("./Packet.js");
|
|
||||||
|
|
||||||
class Packet0KeepAlive extends Packet {
|
|
||||||
writePacket() {
|
|
||||||
super.writePacket();
|
|
||||||
|
|
||||||
return this.toBuffer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Packet0KeepAlive;
|
|
|
@ -1,34 +0,0 @@
|
||||||
/*
|
|
||||||
========- Packet103SetSlot.js -=========
|
|
||||||
Created by Holly (tgpethan) (c) 2021
|
|
||||||
Licenced under MIT
|
|
||||||
========================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
const Packet = require("./Packet.js");
|
|
||||||
|
|
||||||
class Packet103SetSlot extends Packet {
|
|
||||||
constructor(window_id = 0, slot = 0, item_id = -1, item_count = 0, item_uses = 0) {
|
|
||||||
super(0x67);
|
|
||||||
|
|
||||||
this.window_id = window_id;
|
|
||||||
this.slot = slot;
|
|
||||||
this.item_id = item_id;
|
|
||||||
this.item_count = item_count;
|
|
||||||
this.item_uses = item_uses;
|
|
||||||
}
|
|
||||||
|
|
||||||
writePacket() {
|
|
||||||
super.writePacket();
|
|
||||||
|
|
||||||
this.writer.writeByte(this.window_id);
|
|
||||||
this.writer.writeShort(this.slot);
|
|
||||||
this.writer.writeShort(this.item_id);
|
|
||||||
this.writer.writeByte(this.item_count);
|
|
||||||
if (this.item_id != -1) this.writer.writeShort(this.item_uses);
|
|
||||||
|
|
||||||
return this.toBuffer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Packet103SetSlot;
|
|
|
@ -1,26 +0,0 @@
|
||||||
/*
|
|
||||||
=========- Packet10Player.js -==========
|
|
||||||
Created by Holly (tgpethan) (c) 2021
|
|
||||||
Licenced under MIT
|
|
||||||
========================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
const Packet = require("./Packet.js");
|
|
||||||
|
|
||||||
class Packet10Player extends Packet {
|
|
||||||
constructor(onGround = true) {
|
|
||||||
super(0x0A);
|
|
||||||
|
|
||||||
this.onGround = onGround;
|
|
||||||
}
|
|
||||||
|
|
||||||
writePacket() {
|
|
||||||
super.writePacket();
|
|
||||||
|
|
||||||
this.writer.writeBool(this.onGround);
|
|
||||||
|
|
||||||
return this.toBuffer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Packet10Player;
|
|
|
@ -1,38 +0,0 @@
|
||||||
/*
|
|
||||||
==- Packet13PlayerPositionAndLook.js -==
|
|
||||||
Created by Holly (tgpethan) (c) 2021
|
|
||||||
Licenced under MIT
|
|
||||||
========================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
const Packet = require("./Packet.js");
|
|
||||||
|
|
||||||
class Packet13PlayerPositionAndLook extends Packet {
|
|
||||||
constructor(x = 0, y = 65, stance = 67, z = 0, yaw = 0.0, pitch = 0.0, onGround = true) {
|
|
||||||
super(0x0D);
|
|
||||||
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
this.stance = stance;
|
|
||||||
this.z = z;
|
|
||||||
this.yaw = yaw;
|
|
||||||
this.pitch = pitch;
|
|
||||||
this.onGround = onGround;
|
|
||||||
}
|
|
||||||
|
|
||||||
writePacket() {
|
|
||||||
super.writePacket();
|
|
||||||
|
|
||||||
this.writer.writeDouble(this.x);
|
|
||||||
this.writer.writeDouble(this.y);
|
|
||||||
this.writer.writeDouble(this.stance);
|
|
||||||
this.writer.writeDouble(this.z);
|
|
||||||
this.writer.writeFloat(this.yaw);
|
|
||||||
this.writer.writeFloat(this.pitch);
|
|
||||||
this.writer.writeBool(this.onGround);
|
|
||||||
|
|
||||||
return this.toBuffer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Packet13PlayerPositionAndLook;
|
|
|
@ -1,28 +0,0 @@
|
||||||
/*
|
|
||||||
========- Packet18Animation.js -========
|
|
||||||
Created by Holly (tgpethan) (c) 2021
|
|
||||||
Licenced under MIT
|
|
||||||
========================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
const Packet = require("./Packet.js");
|
|
||||||
|
|
||||||
class Packet18Animation extends Packet {
|
|
||||||
constructor(EID = 0, animation = 0) {
|
|
||||||
super(0x12);
|
|
||||||
|
|
||||||
this.EID = EID;
|
|
||||||
this.animation = animation;
|
|
||||||
}
|
|
||||||
|
|
||||||
writePacket() {
|
|
||||||
super.writePacket();
|
|
||||||
|
|
||||||
this.writer.writeInt(this.EID);
|
|
||||||
this.writer.writeByte(this.animation);
|
|
||||||
|
|
||||||
return this.toBuffer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Packet18Animation;
|
|
|
@ -1,36 +0,0 @@
|
||||||
/*
|
|
||||||
=======- Packet1LoginRequest.js -=======
|
|
||||||
Created by Holly (tgpethan) (c) 2021
|
|
||||||
Licenced under MIT
|
|
||||||
========================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
const Packet = require("./Packet.js");
|
|
||||||
|
|
||||||
class Packet1LoginRequest extends Packet {
|
|
||||||
constructor(protocol_version = 0, username = "", map_seed = BigInt(0), dimension = 0) {
|
|
||||||
super(0x01);
|
|
||||||
|
|
||||||
this.protocol_version = protocol_version;
|
|
||||||
this.username = username;
|
|
||||||
this.map_seed = map_seed;
|
|
||||||
this.dimension = dimension;
|
|
||||||
}
|
|
||||||
|
|
||||||
readPacket() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
writePacket(EID = 0) {
|
|
||||||
super.writePacket();
|
|
||||||
|
|
||||||
this.writer.writeInt(EID);
|
|
||||||
this.writer.writeString("");
|
|
||||||
this.writer.writeLong(971768181197178410);
|
|
||||||
this.writer.writeByte(0);
|
|
||||||
|
|
||||||
return this.toBuffer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Packet1LoginRequest;
|
|
|
@ -1,41 +0,0 @@
|
||||||
/*
|
|
||||||
=====- Packet20NamedEntitySpawn.js -====
|
|
||||||
Created by Holly (tgpethan) (c) 2021
|
|
||||||
Licenced under MIT
|
|
||||||
========================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
const Packet = require("./Packet.js");
|
|
||||||
const Converter = require("../Converter.js");
|
|
||||||
|
|
||||||
class Packet20NamedEntitySpawn extends Packet {
|
|
||||||
constructor(EID = 0, entityName = "", x = 0.0, y = 0.0, z = 0.0, yaw = 0.0, pitch = 0.0, currentItem = 0) {
|
|
||||||
super(0x14);
|
|
||||||
|
|
||||||
this.EID = EID;
|
|
||||||
this.entityName = entityName;
|
|
||||||
this.absX = Converter.toAbsoluteInt(x);
|
|
||||||
this.absY = Converter.toAbsoluteInt(y);
|
|
||||||
this.absZ = Converter.toAbsoluteInt(z);
|
|
||||||
this.packedYaw = Converter.to360Fraction(yaw);
|
|
||||||
this.packedPitch = Converter.to360Fraction(pitch);
|
|
||||||
this.currentItem = currentItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
writePacket() {
|
|
||||||
super.writePacket();
|
|
||||||
|
|
||||||
this.writer.writeInt(this.EID);
|
|
||||||
this.writer.writeString(this.entityName);
|
|
||||||
this.writer.writeInt(this.absX);
|
|
||||||
this.writer.writeInt(this.absY);
|
|
||||||
this.writer.writeInt(this.absZ);
|
|
||||||
this.writer.writeUByte(this.packedYaw);
|
|
||||||
this.writer.writeUByte(this.packedPitch);
|
|
||||||
this.writer.writeShort(this.currentItem);
|
|
||||||
|
|
||||||
return this.toBuffer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Packet20NamedEntitySpawn;
|
|
|
@ -1,26 +0,0 @@
|
||||||
/*
|
|
||||||
=========- Packet2Handshake.js -========
|
|
||||||
Created by Holly (tgpethan) (c) 2021
|
|
||||||
Licenced under MIT
|
|
||||||
========================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
const Packet = require("./Packet.js");
|
|
||||||
|
|
||||||
class Packet2Handshake extends Packet {
|
|
||||||
constructor(username = "") {
|
|
||||||
super(0x02);
|
|
||||||
|
|
||||||
this.username = username;
|
|
||||||
}
|
|
||||||
|
|
||||||
writePacket() {
|
|
||||||
super.writePacket();
|
|
||||||
|
|
||||||
this.writer.writeString("-"); // "-" == Offline mode
|
|
||||||
|
|
||||||
return this.toBuffer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Packet2Handshake;
|
|
|
@ -1,33 +0,0 @@
|
||||||
/*
|
|
||||||
=======- Packet32EntityLook.js -========
|
|
||||||
Created by Holly (tgpethan) (c) 2021
|
|
||||||
Licenced under MIT
|
|
||||||
========================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
const Packet = require("./Packet.js");
|
|
||||||
const Converter = require("../Converter.js");
|
|
||||||
|
|
||||||
class Packet32EntityLook extends Packet {
|
|
||||||
constructor(EID = 0, yaw = 0, pitch = 0) {
|
|
||||||
super(0x20);
|
|
||||||
|
|
||||||
this.packetSize = 7;
|
|
||||||
|
|
||||||
this.EID = EID;
|
|
||||||
this.packedYaw = Converter.to360Fraction(yaw);
|
|
||||||
this.packedPitch = Converter.to360Fraction(pitch);
|
|
||||||
}
|
|
||||||
|
|
||||||
writePacket() {
|
|
||||||
super.writePacket();
|
|
||||||
|
|
||||||
this.writer.writeInt(this.EID);
|
|
||||||
this.writer.writeUByte(this.packedYaw);
|
|
||||||
this.writer.writeUByte(this.packedPitch);
|
|
||||||
|
|
||||||
return this.toBuffer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Packet32EntityLook;
|
|
|
@ -1,41 +0,0 @@
|
||||||
/*
|
|
||||||
======- Packet34EntityTeleport.js -=====
|
|
||||||
Created by Holly (tgpethan) (c) 2021
|
|
||||||
Licenced under MIT
|
|
||||||
========================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
const Packet = require("./Packet.js");
|
|
||||||
const Converter = require("../Converter.js");
|
|
||||||
|
|
||||||
class Packet34EntityTeleport extends Packet {
|
|
||||||
constructor(EID = 0, x = 0, y = 0, z = 0, yaw = 0, pitch = 0) {
|
|
||||||
super(0x22);
|
|
||||||
|
|
||||||
this.packetSize = 19;
|
|
||||||
|
|
||||||
this.EID = EID;
|
|
||||||
this.absX = Converter.toAbsoluteInt(x);
|
|
||||||
this.absY = Converter.toAbsoluteInt(y);
|
|
||||||
this.absZ = Converter.toAbsoluteInt(z);
|
|
||||||
this.packedYaw = Converter.to360Fraction(yaw);
|
|
||||||
this.packedPitch = Converter.to360Fraction(pitch);
|
|
||||||
}
|
|
||||||
|
|
||||||
soup() {
|
|
||||||
super.writePacket();
|
|
||||||
this.writer.writeInt(this.EID);
|
|
||||||
}
|
|
||||||
|
|
||||||
writePacket() {
|
|
||||||
this.writer.writeInt(this.absX);
|
|
||||||
this.writer.writeInt(this.absY);
|
|
||||||
this.writer.writeInt(this.absZ);
|
|
||||||
this.writer.writeUByte(this.packedYaw);
|
|
||||||
this.writer.writeUByte(this.packedPitch);
|
|
||||||
|
|
||||||
return this.toBuffer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Packet34EntityTeleport;
|
|
|
@ -1,26 +0,0 @@
|
||||||
/*
|
|
||||||
===========- Packet3Chat.js -===========
|
|
||||||
Created by Holly (tgpethan) (c) 2021
|
|
||||||
Licenced under MIT
|
|
||||||
========================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
const Packet = require("./Packet.js");
|
|
||||||
|
|
||||||
class Packet3Chat extends Packet {
|
|
||||||
constructor(message = "") {
|
|
||||||
super(0x03);
|
|
||||||
|
|
||||||
this.message = message;
|
|
||||||
}
|
|
||||||
|
|
||||||
writePacket() {
|
|
||||||
super.writePacket();
|
|
||||||
|
|
||||||
this.writer.writeString(this.message);
|
|
||||||
|
|
||||||
return this.toBuffer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Packet3Chat;
|
|
|
@ -1,30 +0,0 @@
|
||||||
/*
|
|
||||||
========- Packet4TimeUpdate.js -========
|
|
||||||
Created by Holly (tgpethan) (c) 2021
|
|
||||||
Licenced under MIT
|
|
||||||
========================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
const Packet = require("./Packet.js");
|
|
||||||
|
|
||||||
class Packet4TimeUpdate extends Packet {
|
|
||||||
constructor(time = BigInt(0)) {
|
|
||||||
super(0x04);
|
|
||||||
|
|
||||||
this.time = time;
|
|
||||||
}
|
|
||||||
|
|
||||||
readPacket() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
writePacket() {
|
|
||||||
super.writePacket();
|
|
||||||
|
|
||||||
this.writer.writeLong(this.time);
|
|
||||||
|
|
||||||
return this.toBuffer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Packet4TimeUpdate;
|
|
|
@ -1,30 +0,0 @@
|
||||||
/*
|
|
||||||
========- Packet50PreChunk.js -=========
|
|
||||||
Created by Holly (tgpethan) (c) 2021
|
|
||||||
Licenced under MIT
|
|
||||||
========================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
const Packet = require("./Packet.js");
|
|
||||||
|
|
||||||
class Packet50PreChunk extends Packet {
|
|
||||||
constructor(x = 0, z = 0, mode = true) {
|
|
||||||
super(0x32);
|
|
||||||
|
|
||||||
this.x = x;
|
|
||||||
this.z = z;
|
|
||||||
this.mode = mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
writePacket() {
|
|
||||||
super.writePacket();
|
|
||||||
|
|
||||||
this.writer.writeInt(this.x);
|
|
||||||
this.writer.writeInt(this.z);
|
|
||||||
this.writer.writeBool(this.mode);
|
|
||||||
|
|
||||||
return this.toBuffer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Packet50PreChunk;
|
|
|
@ -1,34 +0,0 @@
|
||||||
/*
|
|
||||||
=======- Packet53BlockChange.js -=======
|
|
||||||
Created by Holly (tgpethan) (c) 2021
|
|
||||||
Licenced under MIT
|
|
||||||
========================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
const Packet = require("./Packet.js");
|
|
||||||
|
|
||||||
class Packet53BlockChange extends Packet {
|
|
||||||
constructor(x = 0, y = 0, z = 0, block_type = 0, block_metadata = 0) {
|
|
||||||
super(0x35);
|
|
||||||
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
this.z = z;
|
|
||||||
this.block_type = block_type;
|
|
||||||
this.block_metadata = block_metadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
writePacket() {
|
|
||||||
super.writePacket();
|
|
||||||
|
|
||||||
this.writer.writeInt(this.x);
|
|
||||||
this.writer.writeByte(this.y);
|
|
||||||
this.writer.writeInt(this.z);
|
|
||||||
this.writer.writeByte(this.block_type);
|
|
||||||
this.writer.writeByte(this.block_metadata);
|
|
||||||
|
|
||||||
return this.toBuffer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Packet53BlockChange;
|
|
|
@ -1,30 +0,0 @@
|
||||||
/*
|
|
||||||
======- Packet6SpawnPosition.js -=======
|
|
||||||
Created by Holly (tgpethan) (c) 2021
|
|
||||||
Licenced under MIT
|
|
||||||
========================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
const Packet = require("./Packet.js");
|
|
||||||
|
|
||||||
class Packet6SpawnPosition extends Packet {
|
|
||||||
constructor(x = 8.5, y = 65.5, z = 8.5) {
|
|
||||||
super(0x06);
|
|
||||||
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
this.z = z;
|
|
||||||
}
|
|
||||||
|
|
||||||
writePacket() {
|
|
||||||
super.writePacket();
|
|
||||||
|
|
||||||
this.writer.writeInt(this.x);
|
|
||||||
this.writer.writeInt(this.y);
|
|
||||||
this.writer.writeInt(this.z);
|
|
||||||
|
|
||||||
return this.toBuffer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Packet6SpawnPosition;
|
|
|
@ -1,91 +0,0 @@
|
||||||
/*
|
|
||||||
===========- funkyArray.js -============
|
|
||||||
Created by Holly (tgpethan) (c) 2021
|
|
||||||
Licenced under MIT
|
|
||||||
========================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
const pRandom = require("./prettyRandom.js");
|
|
||||||
|
|
||||||
module.exports = class {
|
|
||||||
constructor(indexingMode = false) {
|
|
||||||
this.items = {};
|
|
||||||
this.itemKeys = Object.keys(this.items);
|
|
||||||
|
|
||||||
this.indexingMode = indexingMode;
|
|
||||||
this.index = 0;
|
|
||||||
|
|
||||||
this.iterableArray = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
add(item, regenerate = true) {
|
|
||||||
let id;
|
|
||||||
if (this.indexingMode) {
|
|
||||||
this.items[id = this.index] = item;
|
|
||||||
this.index++;
|
|
||||||
} else {
|
|
||||||
this.items[id = pRandom()] = item;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (regenerate) {
|
|
||||||
this.regenerateIterableArray();
|
|
||||||
this.itemKeys = Object.keys(this.items);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.items[id];
|
|
||||||
}
|
|
||||||
|
|
||||||
remove(id, regenerate = true) {
|
|
||||||
delete this.items[id];
|
|
||||||
if (regenerate) {
|
|
||||||
this.regenerateIterableArray();
|
|
||||||
this.itemKeys = Object.keys(this.items);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
removeFirstItem(regenerate = true) {
|
|
||||||
delete this.items[this.itemKeys[0]];
|
|
||||||
if (regenerate) this.regenerateIterableArray();
|
|
||||||
this.itemKeys = Object.keys(this.items);
|
|
||||||
}
|
|
||||||
|
|
||||||
regenerateIterableArray() {
|
|
||||||
this.iterableArray = new Array();
|
|
||||||
for (let itemKey of this.itemKeys) {
|
|
||||||
this.iterableArray.push(this.items[itemKey]);
|
|
||||||
}
|
|
||||||
this.itemKeys = Object.keys(this.items);
|
|
||||||
}
|
|
||||||
|
|
||||||
getFirstItem() {
|
|
||||||
return this.items[this.itemKeys[0]];
|
|
||||||
}
|
|
||||||
|
|
||||||
getLength() {
|
|
||||||
return this.itemKeys.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
getKeyById(id) {
|
|
||||||
return this.itemKeys[id];
|
|
||||||
}
|
|
||||||
|
|
||||||
getById(id) {
|
|
||||||
return this.items[this.itemKeys[id]];
|
|
||||||
}
|
|
||||||
|
|
||||||
getByKey(key) {
|
|
||||||
return this.items[key];
|
|
||||||
}
|
|
||||||
|
|
||||||
getKeys() {
|
|
||||||
return this.itemKeys;
|
|
||||||
}
|
|
||||||
|
|
||||||
getItems() {
|
|
||||||
return this.items;
|
|
||||||
}
|
|
||||||
|
|
||||||
getIterableItems() {
|
|
||||||
return this.iterableArray;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
/*
|
|
||||||
===========- prettyRandom.js -==========
|
|
||||||
Created by Holly (tgpethan) (c) 2021
|
|
||||||
Licenced under MIT
|
|
||||||
========================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
let lastRandom = -1;
|
|
||||||
|
|
||||||
function pRandom(from = 0, to = 2147483647) {
|
|
||||||
let thisRandom = Math.floor(map(Math.random(), 0, 1, from, to));
|
|
||||||
if (thisRandom == lastRandom) thisRandom = pRandom(from, to);
|
|
||||||
|
|
||||||
return thisRandom;
|
|
||||||
}
|
|
||||||
|
|
||||||
function map(input, inputMin, inputMax, outputMin, outputMax) {
|
|
||||||
const newv = (input - inputMin) / (inputMax - inputMin) * (outputMax - outputMin) + outputMin;
|
|
||||||
|
|
||||||
if (outputMin < outputMax) return constrain(newv, outputMin, outputMax);
|
|
||||||
else return constrain(newv, outputMax, outputMin);
|
|
||||||
}
|
|
||||||
|
|
||||||
function constrain(input, low, high) {
|
|
||||||
return Math.max(Math.min(input, high), low);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = pRandom;
|
|
|
@ -1,82 +0,0 @@
|
||||||
/*
|
|
||||||
===========- ChunkWorker.js -===========
|
|
||||||
Created by Holly (tgpethan) (c) 2021
|
|
||||||
Licenced under MIT
|
|
||||||
========================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
const { parentPort } = require('worker_threads'),
|
|
||||||
{ deflateSync } = require("zlib");
|
|
||||||
|
|
||||||
const GeneratorFlat = require("../Generators/GeneratorFlat.js");
|
|
||||||
const GeneratorPerlin = require("../Generators/GeneratorPerlin.js");
|
|
||||||
|
|
||||||
const bufferStuff = require("../bufferStuff.js");
|
|
||||||
|
|
||||||
let busyInterval = null;
|
|
||||||
|
|
||||||
parentPort.on("message", (data) => {
|
|
||||||
// This stops the thread from stopping :)
|
|
||||||
if (busyInterval == null) busyInterval = setInterval(() => {}, 86400000); // Runs once a day
|
|
||||||
|
|
||||||
switch (data[0]) {
|
|
||||||
case "chunk":
|
|
||||||
parentPort.postMessage([data[0], doChunk(data[1]), data[2]]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "generate":
|
|
||||||
const startTime = Date.now();
|
|
||||||
const chunkData = generateChunk(data[1], data[2], data[4]);
|
|
||||||
parentPort.postMessage([data[0], chunkData[0], data[1], data[2], data[3], chunkData[1]]);
|
|
||||||
console.log(`Chunk took ${Date.now() - startTime}ms`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function generateChunk(x = 0, z = 0, seed = 0) {
|
|
||||||
return GeneratorPerlin(x, z, seed);
|
|
||||||
}
|
|
||||||
|
|
||||||
function doChunk(chunk) {
|
|
||||||
const writer = new bufferStuff.Writer(18);
|
|
||||||
|
|
||||||
writer.writeByte(0x33); // Chunk
|
|
||||||
writer.writeInt(chunk[0] << 4); // Chunk X
|
|
||||||
writer.writeShort(0 << 7); // Chunk Y
|
|
||||||
writer.writeInt(chunk[1] << 4); // Chunk Z
|
|
||||||
writer.writeByte(15); // Size X
|
|
||||||
writer.writeByte(127); // Size Y
|
|
||||||
writer.writeByte(15); // Size Z
|
|
||||||
|
|
||||||
// pre-alloc since doing an alloc 98,304 times takes a while yknow.
|
|
||||||
const blocks = new bufferStuff.Writer(32768);
|
|
||||||
const metadata = new bufferStuff.Writer(32768);
|
|
||||||
const lighting = new bufferStuff.Writer(32768);
|
|
||||||
|
|
||||||
let blockMeta = false;
|
|
||||||
for (let x = 0; x < 16; x++) {
|
|
||||||
for (let z = 0; z < 16; z++) {
|
|
||||||
for (let y = 0; y < 128; y++) {
|
|
||||||
blocks.writeByte(chunk[2][y][x][z][0]);
|
|
||||||
if (blockMeta) {
|
|
||||||
metadata.writeNibble(chunk[2][y - 1][x][z][1], chunk[2][y][x][z][1]); // NOTE: This is sorta jank but it does work
|
|
||||||
// Light level 15 for 2 blocks (1111 1111)
|
|
||||||
lighting.writeNibble(15, 15); // TODO: Lighting (Client seems to do it's own (when a block update happens) so it's not top priority)
|
|
||||||
}
|
|
||||||
// Hack for nibble stuff
|
|
||||||
blockMeta = !blockMeta;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// These are hacks for the nibbles
|
|
||||||
blocks.writeBuffer(metadata.buffer);
|
|
||||||
blocks.writeBuffer(lighting.buffer); // Block lighting
|
|
||||||
//blocks.writeBuffer(lighting.buffer); // Sky lighting (Looks like this isn't needed???)
|
|
||||||
|
|
||||||
// We're on another thread we don't care if we halt
|
|
||||||
const deflated = deflateSync(blocks.buffer);
|
|
||||||
writer.writeInt(deflated.length); // Compressed Size
|
|
||||||
writer.writeBuffer(deflated); // Compressed chunk data
|
|
||||||
|
|
||||||
return writer.buffer;
|
|
||||||
}
|
|
39
server/World.ts
Normal file
39
server/World.ts
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import { FunkyArray } from "../funkyArray";
|
||||||
|
import { Chunk } from "./Chunk";
|
||||||
|
import { IEntity } from "./entities/IEntity";
|
||||||
|
import { FlatGenerator } from "./generators/Flat";
|
||||||
|
import { IGenerator } from "./generators/IGenerator";
|
||||||
|
|
||||||
|
export class World {
|
||||||
|
public chunks:FunkyArray<number, Chunk>;
|
||||||
|
public entites:FunkyArray<number, IEntity>;
|
||||||
|
|
||||||
|
public generator:IGenerator;
|
||||||
|
|
||||||
|
public constructor() {
|
||||||
|
this.chunks = new FunkyArray<number, Chunk>();
|
||||||
|
this.entites = new FunkyArray<number, IEntity>();
|
||||||
|
this.generator = new FlatGenerator();
|
||||||
|
this.chunks.set(Chunk.CreateCoordPair(0, 0), new Chunk(this, 0, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
public addEntity(entity:IEntity) {
|
||||||
|
this.entites.set(entity.entityId, entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeEntity(entity:IEntity|number) {
|
||||||
|
if (typeof(entity) === "number") {
|
||||||
|
return this.entites.remove(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.entites.remove(entity.entityId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getChunk(x:number, z:number) {
|
||||||
|
return this.chunks.get(Chunk.CreateCoordPair(x, z));
|
||||||
|
}
|
||||||
|
|
||||||
|
public tick(tickCount:number) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
15
server/blocks/Block.ts
Normal file
15
server/blocks/Block.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
export class Block {
|
||||||
|
public readonly blockId:number;
|
||||||
|
|
||||||
|
public constructor(blockId:number) {
|
||||||
|
this.blockId = blockId;
|
||||||
|
}
|
||||||
|
|
||||||
|
static stone = new Block(1);
|
||||||
|
static grass = new Block(2);
|
||||||
|
static dirt = new Block(3);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static bedrock = new Block(7);
|
||||||
|
}
|
|
@ -1,225 +0,0 @@
|
||||||
/*
|
|
||||||
===========- bufferStuff.js -===========
|
|
||||||
Created by Holly (tgpethan) (c) 2021
|
|
||||||
Licenced under MIT
|
|
||||||
========================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
module.exports.Writer = class {
|
|
||||||
// bufferStuff supports pre-allocating memory for the buffer
|
|
||||||
// if you pass in a size of 0 then it will just dynamicly allocate at the
|
|
||||||
// cost of performance
|
|
||||||
|
|
||||||
// NOTE: In pre-allocation mode if you exceed the size of the buffer
|
|
||||||
// that you set it will cause a crash.
|
|
||||||
constructor(size = 0) {
|
|
||||||
this.buffer = Buffer.alloc(size);
|
|
||||||
this.offset = 0;
|
|
||||||
this.baseSize = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
reset() {
|
|
||||||
this.buffer = Buffer.alloc(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
writeBuffer(buff = Buffer.alloc(0)) {
|
|
||||||
this.buffer = Buffer.concat([this.buffer, buff], this.buffer.length + buff.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
writeBool(data = false) {
|
|
||||||
this.writeByte(data ? 1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: Currently writing a nibble requires you to write both halves at the same time.
|
|
||||||
writeNibble(nibble1 = 0, nibble2 = 0) {
|
|
||||||
this.writeUByte(nibble1 | (nibble2 << 4));
|
|
||||||
}
|
|
||||||
|
|
||||||
writeByte(data = 0) {
|
|
||||||
if (this.baseSize == 0) {
|
|
||||||
const buff = Buffer.alloc(1);
|
|
||||||
buff.writeInt8(data, 0);
|
|
||||||
|
|
||||||
this.writeBuffer(buff);
|
|
||||||
} else {
|
|
||||||
this.buffer.writeInt8(data, this.offset);
|
|
||||||
this.offset += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
writeUByte(data = 0) {
|
|
||||||
if (this.baseSize == 0) {
|
|
||||||
const buff = Buffer.alloc(1);
|
|
||||||
buff.writeUInt8(data, 0);
|
|
||||||
|
|
||||||
this.writeBuffer(buff);
|
|
||||||
} else {
|
|
||||||
this.buffer.writeUInt8(data, this.offset);
|
|
||||||
this.offset += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
writeByteArray(data = [0]) {
|
|
||||||
if (this.baseSize == 0) {
|
|
||||||
const buff = Buffer.alloc(data.length);
|
|
||||||
|
|
||||||
for (let byte of data) {
|
|
||||||
buff.writeInt8(byte);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.writeBuffer(buff);
|
|
||||||
} else {
|
|
||||||
for (let byte of data) {
|
|
||||||
this.buffer.writeInt8(byte);
|
|
||||||
this.offset += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
writeShort(data = 0) {
|
|
||||||
if (this.baseSize == 0) {
|
|
||||||
const buff = Buffer.alloc(2);
|
|
||||||
buff.writeIntBE(data, 0, 2);
|
|
||||||
|
|
||||||
this.writeBuffer(buff);
|
|
||||||
} else {
|
|
||||||
this.buffer.writeIntBE(data, this.offset, 2);
|
|
||||||
this.offset += 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
writeShortArray(data = [0]) {
|
|
||||||
if (this.baseSize == 0) {
|
|
||||||
const buff = Buffer.alloc(data.length * 2);
|
|
||||||
let offset = 0;
|
|
||||||
|
|
||||||
for (let short of data) {
|
|
||||||
buff.writeIntBE(short, offset, 2);
|
|
||||||
offset += 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.writeBuffer(buff);
|
|
||||||
} else {
|
|
||||||
for (let short of data) {
|
|
||||||
this.buffer.writeIntBE(short, this.offset, 2);
|
|
||||||
this.offset += 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
writeInt(data = 0) {
|
|
||||||
if (this.baseSize == 0) {
|
|
||||||
const buff = Buffer.alloc(4);
|
|
||||||
buff.writeIntBE(data, 0, 4);
|
|
||||||
|
|
||||||
this.writeBuffer(buff);
|
|
||||||
} else {
|
|
||||||
this.buffer.writeIntBE(data, this.offset, 4);
|
|
||||||
this.offset += 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
writeLong(data = 0) {
|
|
||||||
if (this.baseSize == 0) {
|
|
||||||
const buff = Buffer.alloc(8);
|
|
||||||
if (data instanceof BigInt) buff.writeBigInt64BE(data, 0);
|
|
||||||
else buff.writeBigInt64BE(BigInt(data), 0);
|
|
||||||
|
|
||||||
this.writeBuffer(buff);
|
|
||||||
} else {
|
|
||||||
if (data instanceof BigInt) this.buffer.writeBigInt64BE(data, this.offset);
|
|
||||||
else this.buffer.writeBigInt64BE(BigInt(data), this.offset);
|
|
||||||
this.offset += 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
writeFloat(data = 0.0) {
|
|
||||||
if (this.baseSize == 0) {
|
|
||||||
const buff = Buffer.alloc(4);
|
|
||||||
buff.writeFloatBE(data, 0);
|
|
||||||
|
|
||||||
this.writeBuffer(buff);
|
|
||||||
} else {
|
|
||||||
this.buffer.writeFloatBE(data, this.offset);
|
|
||||||
this.offset += 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
writeDouble(data = 0.0) {
|
|
||||||
if (this.baseSize == 0) {
|
|
||||||
const buff = Buffer.alloc(8);
|
|
||||||
buff.writeDoubleBE(data, 0);
|
|
||||||
|
|
||||||
this.writeBuffer(buff);
|
|
||||||
} else {
|
|
||||||
this.buffer.writeDoubleBE(data, this.offset);
|
|
||||||
this.offset += 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
writeString(string = "") {
|
|
||||||
this.writeShort(string.length);
|
|
||||||
|
|
||||||
for (let i = 0; i < string.length; i++) {
|
|
||||||
this.writeShort(string.charCodeAt(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports.Reader = class {
|
|
||||||
constructor(buffer = Buffer.alloc(0)) {
|
|
||||||
this.buffer = buffer;
|
|
||||||
this.offset = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
readBool() {
|
|
||||||
return this.readByte() == 0x01 ? true : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
readByte() {
|
|
||||||
const data = this.buffer.readInt8(this.offset);
|
|
||||||
this.offset += 1;
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
readShort() {
|
|
||||||
const data = this.buffer.readIntBE(this.offset, 2);
|
|
||||||
this.offset += 2;
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
readInt() {
|
|
||||||
const data = this.buffer.readIntBE(this.offset, 4);
|
|
||||||
this.offset += 4;
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
readLong() {
|
|
||||||
const data = this.buffer.readBigInt64BE(this.offset);
|
|
||||||
this.offset += 8;
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
readFloat() {
|
|
||||||
const data = this.buffer.readFloatBE(this.offset);
|
|
||||||
this.offset += 4;
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
readDouble() {
|
|
||||||
const data = this.buffer.readDoubleBE(this.offset);
|
|
||||||
this.offset += 8;
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
readString() {
|
|
||||||
const length = this.readShort();
|
|
||||||
let data = "";
|
|
||||||
|
|
||||||
for (let i = 0; i < length; i++) {
|
|
||||||
data += String.fromCharCode(this.readShort());
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,130 +0,0 @@
|
||||||
/*
|
|
||||||
===========- chunkManager.js -==========
|
|
||||||
Created by Holly (tgpethan) (c) 2021
|
|
||||||
Licenced under MIT
|
|
||||||
========================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
const { Worker } = require('worker_threads');
|
|
||||||
const FunkyArray = require("./Util/funkyArray.js");
|
|
||||||
const pRandom = require("./Util/prettyRandom.js");
|
|
||||||
const config = require("../config.json");
|
|
||||||
|
|
||||||
const workerPath = `${__dirname}/Workers/ChunkWorker.js`;
|
|
||||||
|
|
||||||
module.exports = class {
|
|
||||||
constructor() {
|
|
||||||
this.chunks = {};
|
|
||||||
|
|
||||||
this.queuedBlockUpdates = new FunkyArray();
|
|
||||||
|
|
||||||
global.generatingChunks = true;
|
|
||||||
|
|
||||||
this.threadPool = [];
|
|
||||||
this.workPool = new FunkyArray();
|
|
||||||
this.toRemove = [];
|
|
||||||
|
|
||||||
// TODO: Figure out a better way of doing this?
|
|
||||||
this.seed = pRandom(-2147483647, 2147483647) - new Date().getTime() * pRandom(1, 6);
|
|
||||||
|
|
||||||
// WoAh!!! Thread pool in js!?!??!???!11!?!?!
|
|
||||||
for (let i = 0; i < config.worldThreads; i++) {
|
|
||||||
const worker = new Worker(workerPath);
|
|
||||||
this.threadPool.push([false, worker]);
|
|
||||||
const myID = i;
|
|
||||||
worker.on("message", (data) => {
|
|
||||||
let user;
|
|
||||||
switch (data[0]) {
|
|
||||||
case "chunk":
|
|
||||||
user = global.getUserByKey(data[2]);
|
|
||||||
user.chunksToSend.add(Buffer.from(data[1]));
|
|
||||||
this.toRemove.push(data[1]);
|
|
||||||
this.threadPool[myID][0] = false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "generate":
|
|
||||||
this.chunks[data[2]][data[3]] = data[1];
|
|
||||||
this.toRemove.push(data[4]);
|
|
||||||
const treeBlocksRef = data[5];
|
|
||||||
treeBlocksRef.forEach((block) => {
|
|
||||||
this.setBlock(block[0], block[1], block[2], block[3], 0, false);
|
|
||||||
});
|
|
||||||
this.queuedBlockUpdates.regenerateIterableArray();
|
|
||||||
this.threadPool[myID][0] = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("Created thread pool with " + this.threadPool.length + " threads");
|
|
||||||
|
|
||||||
setInterval(() => {
|
|
||||||
if (this.workPool.getLength() > 0) {
|
|
||||||
let limit = Math.min(this.workPool.getLength(), this.threadPool.length);
|
|
||||||
for (let i = 0; i < limit; i++) {
|
|
||||||
for (let i1 = 0; i1 < this.threadPool.length; i1++) {
|
|
||||||
let thread = this.threadPool[i1];
|
|
||||||
if (!thread[0]) {
|
|
||||||
const key = this.workPool.itemKeys[i];
|
|
||||||
const item = this.workPool.getByKey(key);
|
|
||||||
// Already being processed
|
|
||||||
if (item == null) break;
|
|
||||||
if (item[0] == true) {
|
|
||||||
limit += 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
item[0] = true;
|
|
||||||
item[1][3] = key;
|
|
||||||
thread[1].postMessage(item[1]);
|
|
||||||
thread[0] = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let item of this.toRemove) {
|
|
||||||
//console.log("removing item " + this.workPool.getByKey(item)[0]);
|
|
||||||
this.workPool.remove(item);
|
|
||||||
}
|
|
||||||
this.toRemove = [];
|
|
||||||
}
|
|
||||||
}, 1000 / 60);
|
|
||||||
|
|
||||||
global.generatingChunks = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Store metadata!
|
|
||||||
createChunk(cx = 0, cz = 0) {
|
|
||||||
if (this.chunks[cx] == null) this.chunks[cx] = {};
|
|
||||||
|
|
||||||
this.workPool.add([false, ["generate", cx, cz, null, this.seed]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
chunkExists(cx = 0, cz = 0) {
|
|
||||||
if (this.chunks[cx] == null) return false;
|
|
||||||
if (this.chunks[cx][cz] == null) return false;
|
|
||||||
|
|
||||||
// In any other case the chunk exists
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
multiBlockChunk(chunkX = 0, chunkZ = 0, user) {
|
|
||||||
this.workPool.add([false, ["chunk", [chunkX, chunkZ, this.chunks[chunkX][chunkZ]], user.id, null]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
setBlock(x = 0, y = 0, z = 0, id = 0, metadata = 0, regenerate = true) {
|
|
||||||
if (y < 0 || y > 127) return console.error("Tried to set a block outside of the world!");
|
|
||||||
|
|
||||||
const chunkX = x >> 4;
|
|
||||||
const chunkZ = z >> 4;
|
|
||||||
const blockX = x - (chunkX << 4);
|
|
||||||
const blockZ = z - (chunkZ << 4);
|
|
||||||
|
|
||||||
// Don't queue a block update if that block is already this block (wow those ifs)
|
|
||||||
if (this.chunks[chunkX] != null)
|
|
||||||
if (this.chunks[chunkX][chunkZ] != null)
|
|
||||||
if (this.chunks[chunkX][chunkZ][y][blockX][blockZ] == id) return;
|
|
||||||
|
|
||||||
this.queuedBlockUpdates.add([chunkX, chunkZ, y, blockX, blockZ, id, metadata], regenerate);
|
|
||||||
}
|
|
||||||
}
|
|
23
server/entities/Entity.ts
Normal file
23
server/entities/Entity.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import { World } from "../World";
|
||||||
|
import { IEntity } from "./IEntity";
|
||||||
|
|
||||||
|
export class Entity implements IEntity {
|
||||||
|
public static nextEntityId:number = 0;
|
||||||
|
|
||||||
|
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 constructor(world:World) {
|
||||||
|
this.entityId = Entity.nextEntityId++;
|
||||||
|
|
||||||
|
this.world = world;
|
||||||
|
this.x = this.y = this.z = this.lastX = this.lastY = this.lastZ = 0;
|
||||||
|
}
|
||||||
|
}
|
3
server/entities/IEntity.ts
Normal file
3
server/entities/IEntity.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export interface IEntity {
|
||||||
|
entityId:number
|
||||||
|
}
|
18
server/entities/Player.ts
Normal file
18
server/entities/Player.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import { MinecraftServer } from "../MinecraftServer";
|
||||||
|
import { World } from "../World";
|
||||||
|
import { Entity } from "./Entity";
|
||||||
|
|
||||||
|
export class Player extends Entity {
|
||||||
|
public username:string;
|
||||||
|
private server:MinecraftServer;
|
||||||
|
|
||||||
|
public constructor(server:MinecraftServer, world:World, username:string) {
|
||||||
|
super(world);
|
||||||
|
this.server = server;
|
||||||
|
|
||||||
|
this.username = username;
|
||||||
|
this.x = 8;
|
||||||
|
this.y = 64;
|
||||||
|
this.z = 8;
|
||||||
|
}
|
||||||
|
}
|
21
server/enums/Packets.ts
Normal file
21
server/enums/Packets.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
export enum Packets {
|
||||||
|
KeepAlive = 0x00,
|
||||||
|
LoginRequest = 0x01,
|
||||||
|
Handshake = 0x02,
|
||||||
|
Chat = 0x03,
|
||||||
|
TimeUpdate = 0x04,
|
||||||
|
EntityEquipment = 0x05,
|
||||||
|
SpawnPosition = 0x06,
|
||||||
|
UseEntity = 0x07,
|
||||||
|
UpdateHealth = 0x08,
|
||||||
|
Respawn = 0x09,
|
||||||
|
Player = 0x0A,
|
||||||
|
PlayerPosition = 0x0B,
|
||||||
|
PlayerLook = 0x0C,
|
||||||
|
PlayerPositionLook = 0x0D,
|
||||||
|
|
||||||
|
PreChunk = 0x32,
|
||||||
|
MapChunk = 0x33,
|
||||||
|
|
||||||
|
DisconnectKick = 255
|
||||||
|
}
|
23
server/generators/Flat.ts
Normal file
23
server/generators/Flat.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import { Block } from "../blocks/Block";
|
||||||
|
import { Chunk } from "../Chunk";
|
||||||
|
import { IGenerator } from "./IGenerator";
|
||||||
|
|
||||||
|
export class FlatGenerator implements IGenerator {
|
||||||
|
public generate(chunk:Chunk) {
|
||||||
|
for (let x = 0; x < 16; x++) {
|
||||||
|
for (let z = 0; z < 16; z++) {
|
||||||
|
for (let y = 0; y < 128; y++) {
|
||||||
|
if (y === 63) {
|
||||||
|
chunk.setBlock(Block.grass.blockId, x, y, z);
|
||||||
|
} else if (y === 62 || y === 61) {
|
||||||
|
chunk.setBlock(Block.dirt.blockId, x, y, z);
|
||||||
|
} else if (y === 0) {
|
||||||
|
chunk.setBlock(Block.bedrock.blockId, x, y, z);
|
||||||
|
} else if (y < 61) {
|
||||||
|
chunk.setBlock(Block.stone.blockId, x, y, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5
server/generators/IGenerator.ts
Normal file
5
server/generators/IGenerator.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import { Chunk } from "../Chunk";
|
||||||
|
|
||||||
|
export interface IGenerator {
|
||||||
|
generate: (chunk:Chunk) => void
|
||||||
|
}
|
22
server/packets/Chat.ts
Normal file
22
server/packets/Chat.ts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import { Reader, Writer } from "../../bufferStuff";
|
||||||
|
import { Packets } from "../enums/Packets";
|
||||||
|
import { IPacket } from "./IPacket";
|
||||||
|
|
||||||
|
export class PacketChat implements IPacket {
|
||||||
|
public packetId = Packets.Chat;
|
||||||
|
public message:string;
|
||||||
|
|
||||||
|
public constructor(message:string) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readData(reader:Reader) {
|
||||||
|
this.message = reader.readString();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public writeData() {
|
||||||
|
return new Writer(3 + this.message.length * 2).writeUByte(this.packetId).writeString(this.message).toBuffer();
|
||||||
|
}
|
||||||
|
}
|
20
server/packets/DisconnectKick.ts
Normal file
20
server/packets/DisconnectKick.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { Reader, Writer } from "../../bufferStuff";
|
||||||
|
import { Packets } from "../enums/Packets";
|
||||||
|
import { IPacket } from "./IPacket";
|
||||||
|
|
||||||
|
export class PacketDisconnectKick implements IPacket {
|
||||||
|
public packetId = Packets.DisconnectKick;
|
||||||
|
public reason:string;
|
||||||
|
|
||||||
|
public constructor(reason:string) {
|
||||||
|
this.reason = reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readData(reader:Reader) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public writeData() {
|
||||||
|
return new Writer(3 + this.reason.length * 2).writeUByte(this.packetId).writeString(this.reason).toBuffer();
|
||||||
|
}
|
||||||
|
}
|
31
server/packets/EntityEquipment.ts
Normal file
31
server/packets/EntityEquipment.ts
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import { Reader, Writer } from "../../bufferStuff";
|
||||||
|
import { Packets } from "../enums/Packets";
|
||||||
|
import { IPacket } from "./IPacket";
|
||||||
|
|
||||||
|
export class PacketEntityEquipment implements IPacket {
|
||||||
|
public packetId = Packets.EntityEquipment;
|
||||||
|
public entityId:number;
|
||||||
|
public slot:number;
|
||||||
|
public itemId:number;
|
||||||
|
public damage:number;
|
||||||
|
|
||||||
|
public constructor(entityId:number, slot:number, itemId:number, damage:number) {
|
||||||
|
this.entityId = entityId;
|
||||||
|
this.slot = slot;
|
||||||
|
this.itemId = itemId;
|
||||||
|
this.damage = damage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readData(reader:Reader) {
|
||||||
|
this.entityId = reader.readInt();
|
||||||
|
this.slot = reader.readShort();
|
||||||
|
this.itemId = reader.readShort();
|
||||||
|
this.damage = reader.readShort();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public writeData() {
|
||||||
|
return new Writer(10).writeUByte(this.packetId).writeInt(this.entityId).writeShort(this.slot).writeShort(this.itemId).writeShort(this.damage).toBuffer();
|
||||||
|
}
|
||||||
|
}
|
26
server/packets/Handshake.ts
Normal file
26
server/packets/Handshake.ts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import { Reader, Writer } from "../../bufferStuff";
|
||||||
|
import { Packets } from "../enums/Packets";
|
||||||
|
import { IPacket } from "./IPacket";
|
||||||
|
|
||||||
|
export class PacketHandshake implements IPacket {
|
||||||
|
public packetId:Packets = Packets.Handshake;
|
||||||
|
private username:string;
|
||||||
|
|
||||||
|
public constructor(username?:string) {
|
||||||
|
if (typeof(username) === "string") {
|
||||||
|
this.username = username;
|
||||||
|
} else {
|
||||||
|
this.username = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readData(reader:Reader) {
|
||||||
|
this.username = reader.readString();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public writeData() {
|
||||||
|
return new Writer(5).writeUByte(this.packetId).writeString("-").toBuffer();
|
||||||
|
}
|
||||||
|
}
|
8
server/packets/IPacket.ts
Normal file
8
server/packets/IPacket.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import { Reader, Writer } from "../../bufferStuff";
|
||||||
|
import { Packets } from "../enums/Packets";
|
||||||
|
|
||||||
|
export interface IPacket {
|
||||||
|
packetId: Packets,
|
||||||
|
readData: (reader:Reader) => IPacket,
|
||||||
|
writeData: () => Buffer|Promise<Buffer>
|
||||||
|
}
|
16
server/packets/KeepAlive.ts
Normal file
16
server/packets/KeepAlive.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import { Reader, Writer } from "../../bufferStuff";
|
||||||
|
import { Packets } from "../enums/Packets";
|
||||||
|
import { IPacket } from "./IPacket";
|
||||||
|
|
||||||
|
export class PacketKeepAlive implements IPacket {
|
||||||
|
public packetId = Packets.KeepAlive;
|
||||||
|
private static readonly KeepAliveBuffer:Buffer = new Writer(1).writeByte(Packets.KeepAlive).toBuffer();
|
||||||
|
|
||||||
|
public readData(reader:Reader) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public writeData() {
|
||||||
|
return PacketKeepAlive.KeepAliveBuffer;
|
||||||
|
}
|
||||||
|
}
|
38
server/packets/LoginRequest.ts
Normal file
38
server/packets/LoginRequest.ts
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import { Reader, Writer } from "../../bufferStuff";
|
||||||
|
import { Packets } from "../enums/Packets";
|
||||||
|
import { IPacket } from "./IPacket";
|
||||||
|
|
||||||
|
export class PacketLoginRequest implements IPacket {
|
||||||
|
public packetId = Packets.LoginRequest;
|
||||||
|
public protocolVersion:number;
|
||||||
|
public username:string;
|
||||||
|
public mapSeed:number;
|
||||||
|
public dimension:number;
|
||||||
|
|
||||||
|
public constructor(protocolVersion?:number, username?:string, mapSpeed?:number, dimension?:number) {
|
||||||
|
if (typeof(protocolVersion) === "number" && typeof(username) === "string" && typeof(mapSpeed) === "number" && typeof(dimension) === "number") {
|
||||||
|
this.protocolVersion = protocolVersion;
|
||||||
|
this.username = username;
|
||||||
|
this.mapSeed = mapSpeed;
|
||||||
|
this.dimension = dimension;
|
||||||
|
} else {
|
||||||
|
this.protocolVersion = Number.MIN_VALUE;
|
||||||
|
this.username = "";
|
||||||
|
this.mapSeed = Number.MIN_VALUE;
|
||||||
|
this.dimension = Number.MIN_VALUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readData(reader:Reader) {
|
||||||
|
this.protocolVersion = reader.readInt();
|
||||||
|
this.username = reader.readString();
|
||||||
|
this.mapSeed = Number(reader.readLong());
|
||||||
|
this.dimension = reader.readByte();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public writeData() {
|
||||||
|
return new Writer(16 + (2 * this.username.length)).writeUByte(this.packetId).writeInt(this.protocolVersion).writeString(this.username).writeLong(this.mapSeed).writeByte(this.dimension).toBuffer();
|
||||||
|
}
|
||||||
|
}
|
68
server/packets/MapChunk.ts
Normal file
68
server/packets/MapChunk.ts
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
import { deflate } from "zlib";
|
||||||
|
import { Reader, Writer } from "../../bufferStuff";
|
||||||
|
import { Packets } from "../enums/Packets";
|
||||||
|
import { IPacket } from "./IPacket";
|
||||||
|
import { Chunk } from "../Chunk";
|
||||||
|
|
||||||
|
export class PacketMapChunk implements IPacket {
|
||||||
|
public packetId = Packets.MapChunk;
|
||||||
|
public x:number;
|
||||||
|
public y:number;
|
||||||
|
public z:number;
|
||||||
|
public sizeX:number;
|
||||||
|
public sizeY:number;
|
||||||
|
public sizeZ:number;
|
||||||
|
public chunk:Chunk;
|
||||||
|
|
||||||
|
public constructor(x:number, y:number, z:number, sizeX:number, sizeY:number, sizeZ:number, chunk:Chunk) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.z = z;
|
||||||
|
this.sizeX = sizeX;
|
||||||
|
this.sizeY = sizeY;
|
||||||
|
this.sizeZ = sizeZ;
|
||||||
|
this.chunk = chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readData(reader:Reader) {
|
||||||
|
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public writeData() {
|
||||||
|
return new Promise<Buffer>((resolve, reject) => {
|
||||||
|
const blocks = new Writer(32768);
|
||||||
|
const metadata = new Writer(16384);
|
||||||
|
const lighting = new Writer(32768);
|
||||||
|
|
||||||
|
let blockMeta = false;
|
||||||
|
for (let x = 0; x < 16; x++) {
|
||||||
|
for (let z = 0; z < 16; z++) {
|
||||||
|
for (let y = 0; y < 128; y++) {
|
||||||
|
blocks.writeUByte(this.chunk.getBlockId(x, y, z));
|
||||||
|
if (blockMeta) {
|
||||||
|
metadata.writeUByte(0);
|
||||||
|
// Light level 15 for 2 blocks (1111 1111)
|
||||||
|
lighting.writeUByte(0xff); // TODO: Lighting (Client seems to do it's own (when a block update happens) so it's not top priority)
|
||||||
|
lighting.writeUByte(0xff);
|
||||||
|
}
|
||||||
|
// Hack for nibble stuff
|
||||||
|
blockMeta = !blockMeta;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write meta and lighting data into block buffer for compression
|
||||||
|
blocks.writeBuffer(metadata.toBuffer()).writeBuffer(lighting.toBuffer());
|
||||||
|
|
||||||
|
deflate(blocks.toBuffer(), (err, data) => {
|
||||||
|
if (err) {
|
||||||
|
return reject(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(new Writer(18).writeUByte(this.packetId).writeInt(this.x).writeShort(this.y).writeInt(this.z).writeUByte(this.sizeX).writeUByte(this.sizeY).writeUByte(this.sizeZ).writeInt(data.length).writeBuffer(data).toBuffer());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
22
server/packets/Player.ts
Normal file
22
server/packets/Player.ts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import { Reader, Writer } from "../../bufferStuff";
|
||||||
|
import { Packets } from "../enums/Packets";
|
||||||
|
import { IPacket } from "./IPacket";
|
||||||
|
|
||||||
|
export class PacketPlayer implements IPacket {
|
||||||
|
public packetId = Packets.Player;
|
||||||
|
public onGround:boolean;
|
||||||
|
|
||||||
|
public constructor(onGround:boolean = false) {
|
||||||
|
this.onGround = onGround;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readData(reader:Reader) {
|
||||||
|
this.onGround = reader.readBool();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public writeData() {
|
||||||
|
return new Writer(2).writeUByte(this.packetId).writeBool(this.onGround).toBuffer();
|
||||||
|
}
|
||||||
|
}
|
28
server/packets/PlayerLook.ts
Normal file
28
server/packets/PlayerLook.ts
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import { Reader, Writer } from "../../bufferStuff";
|
||||||
|
import { Packets } from "../enums/Packets";
|
||||||
|
import { IPacket } from "./IPacket";
|
||||||
|
|
||||||
|
export class PacketPlayerLook implements IPacket {
|
||||||
|
public packetId = Packets.Player;
|
||||||
|
public yaw:number;
|
||||||
|
public pitch:number;
|
||||||
|
public onGround:boolean;
|
||||||
|
|
||||||
|
public constructor(yaw:number, pitch:number, onGround:boolean = false) {
|
||||||
|
this.yaw = yaw;
|
||||||
|
this.pitch = pitch;
|
||||||
|
this.onGround = onGround;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readData(reader:Reader) {
|
||||||
|
this.yaw = reader.readFloat();
|
||||||
|
this.pitch = reader.readFloat();
|
||||||
|
this.onGround = reader.readBool();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public writeData() {
|
||||||
|
return new Writer(10).writeUByte(this.packetId).writeFloat(this.yaw).writeFloat(this.pitch).writeBool(this.onGround).toBuffer();
|
||||||
|
}
|
||||||
|
}
|
34
server/packets/PlayerPosition.ts
Normal file
34
server/packets/PlayerPosition.ts
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import { Reader, Writer } from "../../bufferStuff";
|
||||||
|
import { Packets } from "../enums/Packets";
|
||||||
|
import { IPacket } from "./IPacket";
|
||||||
|
|
||||||
|
export class PacketPlayerPosition implements IPacket {
|
||||||
|
public packetId = Packets.PlayerPosition;
|
||||||
|
public x:number;
|
||||||
|
public y:number;
|
||||||
|
public stance:number;
|
||||||
|
public z:number;
|
||||||
|
public onGround:boolean;
|
||||||
|
|
||||||
|
public constructor(x:number, y:number, stance:number, z:number, onGround:boolean = false) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.stance = stance;
|
||||||
|
this.z = z;
|
||||||
|
this.onGround = onGround;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readData(reader:Reader) {
|
||||||
|
this.x = reader.readDouble();
|
||||||
|
this.y = reader.readDouble();
|
||||||
|
this.stance = reader.readDouble();
|
||||||
|
this.z = reader.readDouble();
|
||||||
|
this.onGround = reader.readBool();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public writeData() {
|
||||||
|
return new Writer(34).writeUByte(this.packetId).writeDouble(this.x).writeDouble(this.y).writeDouble(this.stance).writeDouble(this.z).writeBool(this.onGround).toBuffer();
|
||||||
|
}
|
||||||
|
}
|
40
server/packets/PlayerPositionLook.ts
Normal file
40
server/packets/PlayerPositionLook.ts
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import { Reader, Writer } from "../../bufferStuff";
|
||||||
|
import { Packets } from "../enums/Packets";
|
||||||
|
import { IPacket } from "./IPacket";
|
||||||
|
|
||||||
|
export class PacketPlayerPositionLook implements IPacket {
|
||||||
|
public packetId = Packets.PlayerPosition;
|
||||||
|
public x:number;
|
||||||
|
public y:number;
|
||||||
|
public stance:number;
|
||||||
|
public z:number;
|
||||||
|
public yaw:number;
|
||||||
|
public pitch:number;
|
||||||
|
public onGround:boolean;
|
||||||
|
|
||||||
|
public constructor(x:number, y:number, stance:number, z:number, yaw:number, pitch:number, onGround:boolean = false) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.stance = stance;
|
||||||
|
this.z = z;
|
||||||
|
this.yaw = yaw;
|
||||||
|
this.pitch = pitch;
|
||||||
|
this.onGround = onGround;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readData(reader:Reader) {
|
||||||
|
this.x = reader.readDouble();
|
||||||
|
this.y = reader.readDouble();
|
||||||
|
this.stance = reader.readDouble();
|
||||||
|
this.z = reader.readDouble();
|
||||||
|
this.yaw = reader.readFloat();
|
||||||
|
this.pitch = reader.readFloat();
|
||||||
|
this.onGround = reader.readBool();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public writeData() {
|
||||||
|
return new Writer(42).writeUByte(this.packetId).writeDouble(this.x).writeDouble(this.y).writeDouble(this.stance).writeDouble(this.z).writeFloat(this.yaw).writeFloat(this.pitch).writeBool(this.onGround).toBuffer();
|
||||||
|
}
|
||||||
|
}
|
28
server/packets/PreChunk.ts
Normal file
28
server/packets/PreChunk.ts
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import { Reader, Writer } from "../../bufferStuff";
|
||||||
|
import { Packets } from "../enums/Packets";
|
||||||
|
import { IPacket } from "./IPacket";
|
||||||
|
|
||||||
|
export class PacketPreChunk implements IPacket {
|
||||||
|
public packetId = Packets.PreChunk;
|
||||||
|
public x:number;
|
||||||
|
public z:number;
|
||||||
|
public mode:boolean;
|
||||||
|
|
||||||
|
public constructor(x:number, z:number, mode:boolean) {
|
||||||
|
this.x = x;
|
||||||
|
this.z = z;
|
||||||
|
this.mode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readData(reader:Reader) {
|
||||||
|
this.x = reader.readInt();
|
||||||
|
this.z = reader.readInt();
|
||||||
|
this.mode = reader.readBool();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public writeData() {
|
||||||
|
return new Writer(10).writeUByte(this.packetId).writeInt(this.x).writeInt(this.z).writeBool(this.mode).toBuffer();
|
||||||
|
}
|
||||||
|
}
|
22
server/packets/Respawn.ts
Normal file
22
server/packets/Respawn.ts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import { Reader, Writer } from "../../bufferStuff";
|
||||||
|
import { Packets } from "../enums/Packets";
|
||||||
|
import { IPacket } from "./IPacket";
|
||||||
|
|
||||||
|
export class PacketRespawn implements IPacket {
|
||||||
|
public packetId = Packets.Respawn;
|
||||||
|
public health:number;
|
||||||
|
|
||||||
|
public constructor(health:number) {
|
||||||
|
this.health = health;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readData(reader:Reader) {
|
||||||
|
this.health = reader.readShort();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public writeData() {
|
||||||
|
return new Writer(3).writeUByte(this.packetId).writeShort(this.health).toBuffer();
|
||||||
|
}
|
||||||
|
}
|
28
server/packets/SpawnPosition.ts
Normal file
28
server/packets/SpawnPosition.ts
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import { Reader, Writer } from "../../bufferStuff";
|
||||||
|
import { Packets } from "../enums/Packets";
|
||||||
|
import { IPacket } from "./IPacket";
|
||||||
|
|
||||||
|
export class PacketSpawnPosition implements IPacket {
|
||||||
|
public packetId = Packets.SpawnPosition;
|
||||||
|
public x:number;
|
||||||
|
public y:number;
|
||||||
|
public z:number;
|
||||||
|
|
||||||
|
public constructor(x:number, y:number, z:number) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.z = z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readData(reader:Reader) {
|
||||||
|
this.x = reader.readInt();
|
||||||
|
this.y = reader.readInt();
|
||||||
|
this.z = reader.readInt();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public writeData() {
|
||||||
|
return new Writer(13).writeUByte(this.packetId).writeInt(this.x).writeInt(this.y).writeInt(this.z).toBuffer();
|
||||||
|
}
|
||||||
|
}
|
22
server/packets/TimeUpdate.ts
Normal file
22
server/packets/TimeUpdate.ts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import { Reader, Writer } from "../../bufferStuff";
|
||||||
|
import { Packets } from "../enums/Packets";
|
||||||
|
import { IPacket } from "./IPacket";
|
||||||
|
|
||||||
|
export class PacketTimeUpdate implements IPacket {
|
||||||
|
public packetId = Packets.TimeUpdate;
|
||||||
|
public time:number;
|
||||||
|
|
||||||
|
public constructor(time:number) {
|
||||||
|
this.time = time;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readData(reader:Reader) {
|
||||||
|
this.time = Number(reader.readLong());
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public writeData() {
|
||||||
|
return new Writer(9).writeUByte(this.packetId).writeLong(this.time).toBuffer();
|
||||||
|
}
|
||||||
|
}
|
22
server/packets/UpdateHealth.ts
Normal file
22
server/packets/UpdateHealth.ts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import { Reader, Writer } from "../../bufferStuff";
|
||||||
|
import { Packets } from "../enums/Packets";
|
||||||
|
import { IPacket } from "./IPacket";
|
||||||
|
|
||||||
|
export class PacketUpdateHealth implements IPacket {
|
||||||
|
public packetId = Packets.UpdateHealth;
|
||||||
|
public health:number;
|
||||||
|
|
||||||
|
public constructor(health:number) {
|
||||||
|
this.health = health;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readData(reader:Reader) {
|
||||||
|
this.health = reader.readShort();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public writeData() {
|
||||||
|
return new Writer(3).writeUByte(this.packetId).writeShort(this.health).toBuffer();
|
||||||
|
}
|
||||||
|
}
|
28
server/packets/UseEntity.ts
Normal file
28
server/packets/UseEntity.ts
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import { Reader, Writer } from "../../bufferStuff";
|
||||||
|
import { Packets } from "../enums/Packets";
|
||||||
|
import { IPacket } from "./IPacket";
|
||||||
|
|
||||||
|
export class PacketUseEntity implements IPacket {
|
||||||
|
public packetId = Packets.UseEntity;
|
||||||
|
public userId:number;
|
||||||
|
public targetId:number;
|
||||||
|
public leftClick:boolean;
|
||||||
|
|
||||||
|
public constructor(userId:number, targetId:number, leftClick:boolean) {
|
||||||
|
this.userId = userId;
|
||||||
|
this.targetId = targetId;
|
||||||
|
this.leftClick = leftClick;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readData(reader:Reader) {
|
||||||
|
this.userId = reader.readInt();
|
||||||
|
this.targetId = reader.readInt();
|
||||||
|
this.leftClick = reader.readBool();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public writeData() {
|
||||||
|
return new Writer(10).writeUByte(this.packetId).writeInt(this.userId).writeInt(this.targetId).writeBool(this.leftClick).toBuffer();
|
||||||
|
}
|
||||||
|
}
|
357
server/server.js
357
server/server.js
|
@ -1,357 +0,0 @@
|
||||||
/*
|
|
||||||
==============- server.js -=============
|
|
||||||
Created by Holly (tgpethan) (c) 2021
|
|
||||||
Licenced under MIT
|
|
||||||
========================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
const bufferStuff = require("./bufferStuff.js");
|
|
||||||
const ChunkManager = require("./chunkManager.js");
|
|
||||||
const User = require("./user.js");
|
|
||||||
const EntityPlayer = require("./Entities/EntityPlayer.js");
|
|
||||||
const PacketMappingTable = require("./PacketMappingTable.js");
|
|
||||||
const NamedPackets = require("./NamedPackets.js");
|
|
||||||
const Converter = require("./Converter.js");
|
|
||||||
const Block = require("./Blocks/Block.js");
|
|
||||||
|
|
||||||
const Socket = require("net").Socket;
|
|
||||||
|
|
||||||
let idPool = 1;
|
|
||||||
global.fromIDPool = function() {
|
|
||||||
const oldVal = idPool;
|
|
||||||
idPool++;
|
|
||||||
return oldVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
let netUsers = {},
|
|
||||||
netUserKeys = Object.keys(netUsers);
|
|
||||||
|
|
||||||
global.getUserByKey = function(key) {
|
|
||||||
return netUsers[key];
|
|
||||||
}
|
|
||||||
|
|
||||||
global.sendToAllPlayers = function(buffer = Buffer.alloc(0)) {
|
|
||||||
for (let key of netUserKeys) {
|
|
||||||
const user = netUsers[key];
|
|
||||||
user.socket.write(buffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
global.sendToAllPlayersButSelf = function(id, buffer = Buffer.alloc(0)) {
|
|
||||||
for (let key of netUserKeys) {
|
|
||||||
if (key == id) continue;
|
|
||||||
const user = netUsers[key];
|
|
||||||
user.socket.write(buffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function addUser(socket) {
|
|
||||||
let user = new User(global.fromIDPool(), socket);
|
|
||||||
user.entityRef = new EntityPlayer(user, 8.5, 65.5, 8.5);
|
|
||||||
netUsers[user.id] = user;
|
|
||||||
netUserKeys = Object.keys(netUsers);
|
|
||||||
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeUser(id) {
|
|
||||||
delete netUsers[id];
|
|
||||||
netUserKeys = Object.keys(netUsers);
|
|
||||||
}
|
|
||||||
|
|
||||||
let config = {};
|
|
||||||
|
|
||||||
let entities = {};
|
|
||||||
let entityKeys = {};
|
|
||||||
|
|
||||||
global.chunkManager = new ChunkManager();
|
|
||||||
global.generatingChunks = false;
|
|
||||||
|
|
||||||
let tickInterval, tickCounter = BigInt(0), worldTime = 0;
|
|
||||||
let tickRate = BigInt(20);
|
|
||||||
|
|
||||||
module.exports.init = function(config) {
|
|
||||||
config = config;
|
|
||||||
console.log(`Up! Running at 0.0.0.0:${config.port}`);
|
|
||||||
|
|
||||||
tickInterval = setInterval(() => {
|
|
||||||
// Runs every sec
|
|
||||||
if (tickCounter % tickRate == 0) {
|
|
||||||
for (let key of netUserKeys) {
|
|
||||||
const user = netUsers[key];
|
|
||||||
user.socket.write(new PacketMappingTable[NamedPackets.KeepAlive]().writePacket());
|
|
||||||
if (user.loginFinished) user.socket.write(new PacketMappingTable[NamedPackets.TimeUpdate](BigInt(worldTime)).writePacket());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Do chunk updates
|
|
||||||
// Don't update if chunk is generating
|
|
||||||
if (global.chunkManager.queuedBlockUpdates.getLength() > 0) {
|
|
||||||
let itemsToRemove = [];
|
|
||||||
// Do a max of 128 block updates per tick
|
|
||||||
for (let i = 0; i < Math.min(global.chunkManager.queuedBlockUpdates.getLength(), 128); i++) {
|
|
||||||
const chunkUpdateKey = global.chunkManager.queuedBlockUpdates.itemKeys[i];
|
|
||||||
const chunkUpdate = global.chunkManager.queuedBlockUpdates.items[chunkUpdateKey];
|
|
||||||
|
|
||||||
// TODO: Remove this once infinite terrain is in :)
|
|
||||||
if (chunkUpdate[0] < -3 || chunkUpdate[0] > 3 || chunkUpdate[1] < -3 || chunkUpdate[1] > 3) {
|
|
||||||
itemsToRemove.push(chunkUpdateKey, false);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the chunk just plain doesn't exist (yet) skip this one
|
|
||||||
if (global.chunkManager.chunks[chunkUpdate[0]] == null) continue;
|
|
||||||
if (global.chunkManager.chunks[chunkUpdate[0]][chunkUpdate[1]] == null) continue;
|
|
||||||
|
|
||||||
global.chunkManager.chunks[chunkUpdate[0]][chunkUpdate[1]][chunkUpdate[2]][chunkUpdate[3]][chunkUpdate[4]][0] = chunkUpdate[5];
|
|
||||||
global.chunkManager.chunks[chunkUpdate[0]][chunkUpdate[1]][chunkUpdate[2]][chunkUpdate[3]][chunkUpdate[4]][1] = chunkUpdate[6];
|
|
||||||
|
|
||||||
const packet = new PacketMappingTable[NamedPackets.BlockChange](chunkUpdate[3] + (chunkUpdate[0] << 4), chunkUpdate[2], chunkUpdate[4] + (chunkUpdate[1] << 4), chunkUpdate[5], chunkUpdate[6]).writePacket();
|
|
||||||
for (let userKey of netUserKeys) {
|
|
||||||
const user = netUsers[userKey];
|
|
||||||
if (user.loginFinished) user.socket.write(packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
itemsToRemove.push(chunkUpdateKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let item of itemsToRemove) {
|
|
||||||
global.chunkManager.queuedBlockUpdates.remove(item, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
global.chunkManager.queuedBlockUpdates.regenerateIterableArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Entity update!
|
|
||||||
for (let key of netUserKeys) {
|
|
||||||
const user = netUsers[key];
|
|
||||||
|
|
||||||
if (user.loginFinished) user.entityRef.onTick();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send queued chunks to users
|
|
||||||
for (let key of netUserKeys) {
|
|
||||||
const user = netUsers[key];
|
|
||||||
|
|
||||||
if (user.loginFinished) {
|
|
||||||
let itemsToRemove = [];
|
|
||||||
for (let i = 0; i < Math.min(user.chunksToSend.getLength(), 128); i++) {
|
|
||||||
const chunkKey = user.chunksToSend.itemKeys[i];
|
|
||||||
itemsToRemove.push(chunkKey);
|
|
||||||
user.socket.write(user.chunksToSend.items[chunkKey]);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let item of itemsToRemove) {
|
|
||||||
user.chunksToSend.remove(item, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
user.chunksToSend.regenerateIterableArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tickCounter++;
|
|
||||||
worldTime++;
|
|
||||||
}, 1000 / parseInt(tickRate.toString()));
|
|
||||||
|
|
||||||
for (let x = -3; x < 4; x++) {
|
|
||||||
for (let z = -3; z < 4; z++) {
|
|
||||||
global.chunkManager.createChunk(x, z);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports.connection = async function(socket = new Socket) {
|
|
||||||
const thisUser = addUser(socket);
|
|
||||||
|
|
||||||
socket.on('data', function(chunk) {
|
|
||||||
const reader = new bufferStuff.Reader(chunk);
|
|
||||||
|
|
||||||
const packetID = reader.readByte();
|
|
||||||
|
|
||||||
switch(packetID) {
|
|
||||||
case NamedPackets.Disconnect:
|
|
||||||
removeUser(thisUser.id);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NamedPackets.KeepAlive:
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NamedPackets.LoginRequest:
|
|
||||||
socket.write(new PacketMappingTable[NamedPackets.LoginRequest](reader.readInt(), reader.readString(), global.chunkManager.seed, reader.readByte()).writePacket(thisUser.id));
|
|
||||||
socket.write(new PacketMappingTable[NamedPackets.SpawnPosition]().writePacket());
|
|
||||||
|
|
||||||
for (let x = -3; x < 4; x++) {
|
|
||||||
for (let z = -3; z < 4; z++) {
|
|
||||||
socket.write(new PacketMappingTable[NamedPackets.PreChunk](x, z, true).writePacket());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Place a layer of glass under the player so they don't fall n' die
|
|
||||||
for (let x = 0; x < 16; x++) {
|
|
||||||
for (let z = 0; z < 16; z++) {
|
|
||||||
socket.write(new PacketMappingTable[NamedPackets.BlockChange](x, 64, z, Block.glass.blockID, 0).writePacket());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
socket.write(new PacketMappingTable[NamedPackets.Player](true).writePacket());
|
|
||||||
|
|
||||||
const joinMessage = new PacketMappingTable[NamedPackets.ChatMessage](`\u00A7e${thisUser.username} has joined the game`).writePacket();
|
|
||||||
for (let key of netUserKeys) {
|
|
||||||
netUsers[key].socket.write(joinMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
socket.write(new PacketMappingTable[NamedPackets.SetSlot](0, 36, 3, 64, 0).writePacket());
|
|
||||||
|
|
||||||
socket.write(new PacketMappingTable[NamedPackets.PlayerPositionAndLook](8.5, 65 + 1.6200000047683716, 65, 8.5, 0, 0, false).writePacket());
|
|
||||||
|
|
||||||
thisUser.loginFinished = true;
|
|
||||||
|
|
||||||
// Send chunks
|
|
||||||
for (let x = -3; x < 4; x++) {
|
|
||||||
for (let z = -3; z < 4; z++) {
|
|
||||||
global.chunkManager.multiBlockChunk(x, z, thisUser);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send this user to other online user
|
|
||||||
global.sendToAllPlayersButSelf(thisUser.id, new PacketMappingTable[NamedPackets.NamedEntitySpawn](thisUser.id, thisUser.username, thisUser.entityRef.x, thisUser.entityRef.y, thisUser.entityRef.z, thisUser.entityRef.yaw, thisUser.entityRef.pitch, 0).writePacket());
|
|
||||||
|
|
||||||
// send all online users to this user
|
|
||||||
for (let key of netUserKeys) {
|
|
||||||
if (key == thisUser.id) continue;
|
|
||||||
const user = netUsers[key];
|
|
||||||
|
|
||||||
socket.write(new PacketMappingTable[NamedPackets.NamedEntitySpawn](user.id, user.username, user.entityRef.x, user.entityRef.y, user.entityRef.z, user.entityRef.yaw, user.entityRef.pitch, 0).writePacket());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NamedPackets.Handshake:
|
|
||||||
thisUser.username = reader.readString();
|
|
||||||
|
|
||||||
socket.write(new PacketMappingTable[NamedPackets.Handshake](thisUser.username).writePacket());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NamedPackets.ChatMessage:
|
|
||||||
const message = reader.readString();
|
|
||||||
// Hacky commands until I made a command system
|
|
||||||
if (message.startsWith("/")) {
|
|
||||||
const command = message.substring(1, message.length).split(" ");
|
|
||||||
console.log(command);
|
|
||||||
if (command[0] == "time") {
|
|
||||||
if (command.length < 2) {
|
|
||||||
} else if (command[1] == "set") {
|
|
||||||
if (command.length < 3) {
|
|
||||||
} else {
|
|
||||||
switch (command[2]) {
|
|
||||||
case "day":
|
|
||||||
worldTime = (24000 * (worldTime / 24000));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "noon":
|
|
||||||
worldTime = (24000 * (worldTime / 24000)) + 6000;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "sunset":
|
|
||||||
worldTime = (24000 * (worldTime / 24000)) + 12000;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "midnight":
|
|
||||||
worldTime = (24000 * (worldTime / 24000)) + 18000;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Send player's message to all players
|
|
||||||
const cachedPacket = new PacketMappingTable[NamedPackets.ChatMessage](`<${thisUser.username}> ${message}`).writePacket();
|
|
||||||
for (let key of netUserKeys) {
|
|
||||||
netUsers[key].socket.write(cachedPacket);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NamedPackets.PlayerLook:
|
|
||||||
thisUser.entityRef.yaw = reader.readFloat() % 360 % -360;
|
|
||||||
thisUser.entityRef.pitch = reader.readFloat() % 360 % -360;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NamedPackets.PlayerPosition:
|
|
||||||
thisUser.entityRef.x = reader.readDouble();
|
|
||||||
thisUser.entityRef.y = reader.readDouble();
|
|
||||||
reader.readDouble(); // stance
|
|
||||||
thisUser.entityRef.z = reader.readDouble();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NamedPackets.PlayerPositionAndLook:
|
|
||||||
thisUser.entityRef.x = reader.readDouble();
|
|
||||||
thisUser.entityRef.y = reader.readDouble();
|
|
||||||
reader.readDouble(); // stance
|
|
||||||
thisUser.entityRef.z = reader.readDouble();
|
|
||||||
thisUser.entityRef.yaw = reader.readFloat() % 360 % -360;
|
|
||||||
thisUser.entityRef.pitch = reader.readFloat() % 360 % -360;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NamedPackets.Animation:
|
|
||||||
const EID = reader.readInt();
|
|
||||||
const cachedPacket = new PacketMappingTable[NamedPackets.Animation](thisUser.id, reader.readByte()).writePacket();
|
|
||||||
for (let key of netUserKeys) {
|
|
||||||
if (netUsers[key].id !== thisUser.id) netUsers[key].socket.write(cachedPacket);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NamedPackets.PlayerDigging:
|
|
||||||
const status = reader.readByte();
|
|
||||||
|
|
||||||
if (status == 2) {
|
|
||||||
const x = reader.readInt();
|
|
||||||
const y = reader.readByte();
|
|
||||||
const z = reader.readInt();
|
|
||||||
|
|
||||||
global.chunkManager.setBlock(x, y, z, 0);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NamedPackets.PlayerBlockPlacement:
|
|
||||||
const x = reader.readInt();
|
|
||||||
const y = reader.readByte();
|
|
||||||
const z = reader.readInt();
|
|
||||||
let xOff = 0, yOff = 0, zOff = 0;
|
|
||||||
switch (reader.readByte()) { // direction
|
|
||||||
case 0: yOff = -1; break;
|
|
||||||
case 1: yOff = 1; break;
|
|
||||||
case 2: zOff = -1; break;
|
|
||||||
case 3: zOff = 1; break;
|
|
||||||
case 4: xOff = -1; break;
|
|
||||||
case 5: xOff = 1; break;
|
|
||||||
}
|
|
||||||
const block = reader.readShort();
|
|
||||||
|
|
||||||
global.chunkManager.setBlock(x + xOff, y + yOff, z + zOff, block);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NamedPackets.Player:
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
console.log(toHexValue(packetID));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on('end', function() {
|
|
||||||
console.log("Connection closed");
|
|
||||||
removeUser(thisUser.id);
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on('error', function(err) {
|
|
||||||
console.log("Connection error!");
|
|
||||||
removeUser(thisUser.id);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function toHexValue(val = 0x00) {
|
|
||||||
if (val < 16) return `0x0${val.toString(16).toUpperCase()}`;
|
|
||||||
else return `0x${val.toString(16).toUpperCase()}`;
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
/*
|
|
||||||
===============- user.js -==============
|
|
||||||
Created by Holly (tgpethan) (c) 2021
|
|
||||||
Licenced under MIT
|
|
||||||
========================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
const funkyArray = require("./Util/funkyArray.js");
|
|
||||||
|
|
||||||
const Socket = require("net").Socket;
|
|
||||||
|
|
||||||
module.exports = class {
|
|
||||||
constructor(id = 1, socket = new Socket) {
|
|
||||||
this.id = id;
|
|
||||||
this.socket = socket;
|
|
||||||
|
|
||||||
this.username = "UNNAMED";
|
|
||||||
|
|
||||||
this.loginFinished = false;
|
|
||||||
|
|
||||||
this.entityRef = null;
|
|
||||||
|
|
||||||
this.chunksToSend = new funkyArray();
|
|
||||||
}
|
|
||||||
}
|
|
12
tsconfig.json
Normal file
12
tsconfig.json
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "commonjs",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"target": "es6",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"rootDir": "./",
|
||||||
|
"outDir": "./build",
|
||||||
|
"strict": true
|
||||||
|
}
|
||||||
|
}
|
15
webpack.config.js
Normal file
15
webpack.config.js
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
const path = require('path');
|
||||||
|
const nodeExternals = require('webpack-node-externals');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
target: 'node',
|
||||||
|
externals: [ nodeExternals() ],
|
||||||
|
entry: './build/index.js',
|
||||||
|
output: {
|
||||||
|
path: path.join(__dirname, 'bundle'),
|
||||||
|
filename: 'MCBS.js',
|
||||||
|
},
|
||||||
|
optimization: {
|
||||||
|
minimize: true,
|
||||||
|
},
|
||||||
|
};
|
Loading…
Reference in a new issue