@vaitosoi/minejs 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/assets/minecraft/26.2/packets.json +27 -10
- package/dist/base/buffer.d.ts +2 -0
- package/dist/base/buffer.d.ts.map +1 -0
- package/dist/base/buffer.js +12 -0
- package/dist/base/buffer.js.map +1 -0
- package/dist/base/error.d.ts +33 -6
- package/dist/base/error.d.ts.map +1 -1
- package/dist/base/error.js +74 -11
- package/dist/base/error.js.map +1 -1
- package/dist/base/http.d.ts +2 -0
- package/dist/base/http.d.ts.map +1 -0
- package/dist/base/http.js +14 -0
- package/dist/base/http.js.map +1 -0
- package/dist/base/math.d.ts +22 -1
- package/dist/base/math.d.ts.map +1 -1
- package/dist/base/math.js +49 -2
- package/dist/base/math.js.map +1 -1
- package/dist/client/auth.d.ts +41 -7
- package/dist/client/auth.d.ts.map +1 -1
- package/dist/client/auth.js +96 -39
- package/dist/client/auth.js.map +1 -1
- package/dist/client/client.d.ts +50 -5
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/client.js +47 -18
- package/dist/client/client.js.map +1 -1
- package/dist/client/tcp.d.ts +1 -5
- package/dist/client/tcp.d.ts.map +1 -1
- package/dist/client/tcp.js +8 -21
- package/dist/client/tcp.js.map +1 -1
- package/dist/message/link.d.ts +10 -0
- package/dist/message/link.d.ts.map +1 -0
- package/dist/message/link.js +22 -0
- package/dist/message/link.js.map +1 -0
- package/dist/message/sigCache.d.ts +13 -0
- package/dist/message/sigCache.d.ts.map +1 -0
- package/dist/message/sigCache.js +26 -0
- package/dist/message/sigCache.js.map +1 -0
- package/dist/packet/decoder.d.ts +13 -0
- package/dist/packet/decoder.d.ts.map +1 -1
- package/dist/packet/decoder.js +21 -0
- package/dist/packet/decoder.js.map +1 -1
- package/dist/packet/dispatcher.d.ts +8 -1
- package/dist/packet/dispatcher.d.ts.map +1 -1
- package/dist/packet/dispatcher.js +142 -30
- package/dist/packet/dispatcher.js.map +1 -1
- package/dist/physics/player.d.ts +17 -23
- package/dist/physics/player.d.ts.map +1 -1
- package/dist/physics/player.js +59 -61
- package/dist/physics/player.js.map +1 -1
- package/dist/version/codec.d.ts.map +1 -1
- package/dist/version/codec.js +28 -7
- package/dist/version/codec.js.map +1 -1
- package/dist/version/registry.d.ts +1 -1
- package/dist/version/registry.d.ts.map +1 -1
- package/dist/version/registry.js +2 -7
- package/dist/version/registry.js.map +1 -1
- package/dist/world/state.d.ts +26 -2
- package/dist/world/state.d.ts.map +1 -1
- package/dist/world/state.js +58 -3
- package/dist/world/state.js.map +1 -1
- package/package.json +1 -1
- package/src/base/buffer.ts +12 -0
- package/src/base/error.ts +63 -10
- package/src/base/http.ts +10 -0
- package/src/base/math.ts +55 -3
- package/src/client/auth.ts +130 -52
- package/src/client/client.ts +96 -21
- package/src/client/tcp.ts +10 -27
- package/src/message/link.ts +17 -0
- package/src/message/sigCache.ts +25 -0
- package/src/packet/decoder.ts +21 -0
- package/src/packet/dispatcher.ts +169 -35
- package/src/physics/player.ts +83 -58
- package/src/version/codec.ts +32 -11
- package/src/version/registry.ts +2 -7
- package/src/world/state.ts +72 -4
- package/test/client/index.ts +27 -20
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export class MessageLink {
|
|
2
|
+
constructor (
|
|
3
|
+
public index: number,
|
|
4
|
+
public sender: string,
|
|
5
|
+
public session: string
|
|
6
|
+
) { }
|
|
7
|
+
|
|
8
|
+
public static root(sender: string, session: string) {
|
|
9
|
+
return new MessageLink(0, sender, session);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
public static unsinged(sender: string) { return this.root(sender, "null"); }
|
|
13
|
+
|
|
14
|
+
public isDescendantOf(link: MessageLink) {
|
|
15
|
+
return link.index > this.index && this.sender === link.sender && this.session === link.session;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export class SignatureCache {
|
|
2
|
+
private entries: Buffer[];
|
|
3
|
+
|
|
4
|
+
constructor(public capacity: number) {
|
|
5
|
+
this.entries = Array.from({ length: this.capacity });
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
public static default() { return new SignatureCache(128); }
|
|
9
|
+
|
|
10
|
+
public get(index: number) { return this.entries[index]; }
|
|
11
|
+
|
|
12
|
+
public unpack = (messages: { message_id: number, signature?: number[] | undefined }[]) =>
|
|
13
|
+
messages.map(({ message_id: id, signature }) => signature ? Buffer.from(signature) : this.get(id)!);
|
|
14
|
+
|
|
15
|
+
public push(input: Buffer[]) {
|
|
16
|
+
const queue = input.slice();
|
|
17
|
+
const set = new Set(queue.map(v => v.toString("hex")));
|
|
18
|
+
for (let i = 0; i < this.entries.length && queue.length > 0; i++) {
|
|
19
|
+
const entry = !!this.entries[i] && Buffer.from(this.entries[i]!);
|
|
20
|
+
this.entries[i] = queue.pop()!;
|
|
21
|
+
if (entry && !set.has(entry.toString("hex")))
|
|
22
|
+
queue.unshift(entry);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
package/src/packet/decoder.ts
CHANGED
|
@@ -397,6 +397,27 @@ export class BinaryDecoder {
|
|
|
397
397
|
return readX(this);
|
|
398
398
|
}
|
|
399
399
|
|
|
400
|
+
/**
|
|
401
|
+
* Represents a set of IDs in a certain registry (implied by context), either directly (enumerated IDs) or indirectly (tag name).
|
|
402
|
+
* | Field Name | Field Type | Meaning |
|
|
403
|
+
* |------------|------------|---------|
|
|
404
|
+
* | Type | VarInt | Value used to determine the data that follows. It can be either: 0 - Represents a named set of IDs defined by a tag. Anything else - Represents an ad-hoc set of IDs enumerated inline. |
|
|
405
|
+
* | Tag Name | Optional Identifier | The registry tag defining the ID set. Only present if Type is 0. |
|
|
406
|
+
* | IDs | Optional Array of VarInt | An array of registry IDs. Only present if Type is not 0. The size of the array is equal to Type - 1. |
|
|
407
|
+
*/
|
|
408
|
+
public readIdSet() {
|
|
409
|
+
const type = this.readVarInt();
|
|
410
|
+
let tagName: string | null = null,
|
|
411
|
+
ids: number[] | null = null;
|
|
412
|
+
if (type === 0) tagName = this.readString();
|
|
413
|
+
else ids = this.readArray(type - 1, decoder => decoder.readVarInt());
|
|
414
|
+
return {
|
|
415
|
+
type,
|
|
416
|
+
tagName,
|
|
417
|
+
ids
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
|
|
400
421
|
/**
|
|
401
422
|
* Read Chat Type Decoration
|
|
402
423
|
*
|
package/src/packet/dispatcher.ts
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import zod from "zod";
|
|
2
|
-
import { createCipheriv, createDecipheriv, createPublicKey, publicEncrypt, randomBytes, constants as crypto_constants } from "node:crypto";
|
|
2
|
+
import { createCipheriv, createDecipheriv, createPublicKey, publicEncrypt, randomBytes, constants as crypto_constants, createVerify } from "node:crypto";
|
|
3
3
|
import { BlockEntity, ChunkSection, ClientPlayer, ClientStatus, ConnectionState, Entity, GameMode, PaletteContainer, ServerKnownPack, ServerWorld, SharedState } from "../world/state";
|
|
4
4
|
import { BinaryDecoder, getTextFromTextComponent } from "./decoder";
|
|
5
5
|
import { packBlockPos, SectionsPerChunk } from "../client/static";
|
|
6
|
-
import { Vec3 } from "../physics/direction";
|
|
6
|
+
import { Angle, BaseVec3, Vec3 } from "../physics/direction";
|
|
7
7
|
import { ProtocolVersionMapping } from "../version/registry";
|
|
8
8
|
import { TypedEmmiter } from "../base/event";
|
|
9
9
|
import { ClientEvents } from "../client/client";
|
|
10
10
|
import { makeMovementFlag } from "./encoder";
|
|
11
|
+
import { TextComponent } from "../base/typing";
|
|
12
|
+
import { AuthRelatedNotFound, HaveSignatureButNotIndex, MessageLinkNotFound, MissingAuthOption, NotImplemented, UnexpectedValue } from "../base/error";
|
|
13
|
+
import { randomUUIDBytes, uuidToBuffer } from "../base/math";
|
|
14
|
+
import { MessageLink } from "../message/link";
|
|
15
|
+
import { sliceBuffer } from "../base/buffer";
|
|
11
16
|
|
|
12
17
|
function zodParse<Type extends zod.ZodType>(data: object, zod: Type): zod.infer<Type> {
|
|
13
18
|
return zod.parse(data);
|
|
@@ -50,8 +55,8 @@ export class Dispatcher {
|
|
|
50
55
|
};
|
|
51
56
|
}
|
|
52
57
|
|
|
53
|
-
public sendPacket: (packetId: string, data: object) => void = (
|
|
54
|
-
public
|
|
58
|
+
public sendPacket: (packetId: string, data: object) => void = () => { throw new NotImplemented(); };
|
|
59
|
+
public disconnect: (reason: string, reasonRaw?: TextComponent) => void = () => { throw new NotImplemented(); };
|
|
55
60
|
|
|
56
61
|
|
|
57
62
|
public handlePacket(packetId: string, data: object) {
|
|
@@ -65,15 +70,7 @@ export class Dispatcher {
|
|
|
65
70
|
const { reason } = zodParse(data, zod.object({ reason: zod.record(zod.string(), zod.any()) }));
|
|
66
71
|
const text = getTextFromTextComponent(reason);
|
|
67
72
|
|
|
68
|
-
this.
|
|
69
|
-
state.status = ClientStatus.Disconnected;
|
|
70
|
-
state.state = ConnectionState.Disconnected;
|
|
71
|
-
state.pruneStates();
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
this.emit("disconnect", text.toString());
|
|
75
|
-
this.emit("disconnectRaw", reason);
|
|
76
|
-
this.tcpDisconnect();
|
|
73
|
+
this.disconnect(text, reason);
|
|
77
74
|
}
|
|
78
75
|
|
|
79
76
|
private handleEncryption(data: object) {
|
|
@@ -81,22 +78,30 @@ export class Dispatcher {
|
|
|
81
78
|
server_id,
|
|
82
79
|
public_key,
|
|
83
80
|
verify_token,
|
|
84
|
-
|
|
81
|
+
should_authenticate
|
|
85
82
|
} = zodParse(data, zod.object({
|
|
86
83
|
server_id: zod.string(),
|
|
87
84
|
public_key: zod.array(zod.number()),
|
|
88
85
|
verify_token: zod.array(zod.number()),
|
|
89
|
-
|
|
86
|
+
should_authenticate: zod.boolean()
|
|
90
87
|
}));
|
|
91
88
|
|
|
92
89
|
const sharedSecret = randomBytes(16);
|
|
93
|
-
this.state.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
90
|
+
this.state.sharedSecret = sharedSecret;
|
|
91
|
+
this.state.cipher = createCipheriv("aes-128-cfb8", sharedSecret, sharedSecret);
|
|
92
|
+
this.state.decipher = createDecipheriv("aes-128-cfb8", sharedSecret, sharedSecret);
|
|
93
|
+
this.state.useEncryption = true;
|
|
94
|
+
|
|
95
|
+
const publicKey = Buffer.from(public_key),
|
|
96
|
+
verifyToken = Buffer.from(verify_token);
|
|
97
|
+
if (should_authenticate)
|
|
98
|
+
if (!this.state.authClient)
|
|
99
|
+
throw new AuthRelatedNotFound("auth client");
|
|
100
|
+
else
|
|
101
|
+
this.state.authClient.sendAuth(server_id, sharedSecret, publicKey)
|
|
102
|
+
.then(() => this.sendEncryptionResponse(publicKey, verifyToken));
|
|
103
|
+
else
|
|
104
|
+
this.sendEncryptionResponse(publicKey, verifyToken);
|
|
100
105
|
}
|
|
101
106
|
|
|
102
107
|
private handleSetCompression(data: object) {
|
|
@@ -176,13 +181,7 @@ export class Dispatcher {
|
|
|
176
181
|
const { reason } = zodParse(data, zod.object({ reason: zod.record(zod.string(), zod.any()) }));
|
|
177
182
|
const text = getTextFromTextComponent(reason);
|
|
178
183
|
|
|
179
|
-
this.
|
|
180
|
-
this.state.state = ConnectionState.Disconnected;
|
|
181
|
-
this.state.pruneStates();
|
|
182
|
-
|
|
183
|
-
this.emit("disconnect", text.toString());
|
|
184
|
-
this.emit("disconnectRaw", reason);
|
|
185
|
-
this.tcpDisconnect();
|
|
184
|
+
this.disconnect(text, reason);
|
|
186
185
|
}
|
|
187
186
|
|
|
188
187
|
// Play
|
|
@@ -225,6 +224,9 @@ export class Dispatcher {
|
|
|
225
224
|
this.state.player!.gameMode = game_mode;
|
|
226
225
|
this.state.status = ClientStatus.Ready;
|
|
227
226
|
|
|
227
|
+
if (this.state.useEncryption)
|
|
228
|
+
this.sendPlayerSession();
|
|
229
|
+
|
|
228
230
|
this.emit("ready");
|
|
229
231
|
}
|
|
230
232
|
|
|
@@ -640,16 +642,84 @@ export class Dispatcher {
|
|
|
640
642
|
const senderNameText = getTextFromTextComponent(sender_name).toString(),
|
|
641
643
|
targetNameText = target_name ? getTextFromTextComponent(target_name).toString() : undefined;
|
|
642
644
|
|
|
643
|
-
|
|
645
|
+
const emitObject = {
|
|
644
646
|
sender: senderNameText,
|
|
645
647
|
target: targetNameText,
|
|
646
|
-
content:
|
|
648
|
+
content: getTextFromTextComponent(unsigned_content),
|
|
647
649
|
raw: {
|
|
648
650
|
sender: sender_name,
|
|
649
651
|
target: target_name || undefined,
|
|
650
652
|
content: unsigned_content || undefined,
|
|
651
653
|
}
|
|
652
|
-
}
|
|
654
|
+
};
|
|
655
|
+
|
|
656
|
+
let link: MessageLink | undefined = undefined;
|
|
657
|
+
if (this.state.clientOptions.shouldVerifyMessageOrder && this.state.messageLinks) {
|
|
658
|
+
if (index === 0) {
|
|
659
|
+
if (this.state.sessionID)
|
|
660
|
+
link = MessageLink.root(sender, this.state.sessionID.toString("hex"));
|
|
661
|
+
else
|
|
662
|
+
link = MessageLink.unsinged(sender);
|
|
663
|
+
this.state.messageLinks[sender] = link;
|
|
664
|
+
} else {
|
|
665
|
+
const oldLink = this.state.messageLinks[sender];
|
|
666
|
+
if (!oldLink)
|
|
667
|
+
throw new MessageLinkNotFound(sender);
|
|
668
|
+
let newLink;
|
|
669
|
+
if (this.state.sessionID)
|
|
670
|
+
newLink = new MessageLink(global_index, sender, this.state.sessionID.toString("hex"));
|
|
671
|
+
else
|
|
672
|
+
newLink = MessageLink.unsinged(sender);
|
|
673
|
+
if (!newLink.isDescendantOf(oldLink))
|
|
674
|
+
throw new UnexpectedValue("message order", newLink.index.toString(), (oldLink.index + 1).toString());
|
|
675
|
+
this.state.messageLinks[sender] = newLink;
|
|
676
|
+
link = newLink;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
if (this.state.clientOptions.shouldVerifyMessageSignature === true && message_signature_bytes) {
|
|
681
|
+
if (!this.state.sessionID)
|
|
682
|
+
throw new AuthRelatedNotFound("session uuid");
|
|
683
|
+
if (!this.state.messageSignatureCache)
|
|
684
|
+
throw new AuthRelatedNotFound("signature cache");
|
|
685
|
+
if (!link)
|
|
686
|
+
throw new HaveSignatureButNotIndex();
|
|
687
|
+
const { publicKey } = this.state.getSignature();
|
|
688
|
+
|
|
689
|
+
const formatVersion = Buffer.alloc(4); formatVersion.writeInt32BE(1);
|
|
690
|
+
const senderUUIDBuf = uuidToBuffer(sender);
|
|
691
|
+
const sessionUUIDBuf = this.state.sessionID;
|
|
692
|
+
const indexBuf = Buffer.alloc(4); indexBuf.writeInt32BE(link.index);
|
|
693
|
+
const saltBuf = Buffer.alloc(8); saltBuf.writeBigInt64BE(salt);
|
|
694
|
+
const timestampBuf = Buffer.alloc(8); timestampBuf.writeBigInt64BE(timestamp);
|
|
695
|
+
const _messageBuf = Buffer.from(message, "utf-8");
|
|
696
|
+
const lengthBuf = Buffer.alloc(4); lengthBuf.writeInt32BE(_messageBuf.length);
|
|
697
|
+
const messageBuf = _messageBuf;
|
|
698
|
+
const previousMessagesLenghtBuf = Buffer.alloc(4); previousMessagesLenghtBuf.writeInt32BE(previous_messages.length);
|
|
699
|
+
const previousMessagesBuf = Buffer.concat(this.state.messageSignatureCache.unpack(previous_messages));
|
|
700
|
+
const signaturePayload = Buffer.concat([
|
|
701
|
+
formatVersion,
|
|
702
|
+
senderUUIDBuf,
|
|
703
|
+
sessionUUIDBuf,
|
|
704
|
+
indexBuf,
|
|
705
|
+
saltBuf,
|
|
706
|
+
timestampBuf,
|
|
707
|
+
lengthBuf,
|
|
708
|
+
messageBuf,
|
|
709
|
+
previousMessagesLenghtBuf,
|
|
710
|
+
previousMessagesBuf
|
|
711
|
+
]);
|
|
712
|
+
|
|
713
|
+
const verify = createVerify("RSA-SHA256");
|
|
714
|
+
verify.update(signaturePayload);
|
|
715
|
+
verify.end();
|
|
716
|
+
const signature = Buffer.from(message_signature_bytes);
|
|
717
|
+
|
|
718
|
+
if (!verify.verify(publicKey!, signature))
|
|
719
|
+
this.emit("failedMessage", emitObject);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
this.emit("message", emitObject);
|
|
653
723
|
}
|
|
654
724
|
|
|
655
725
|
private handleSystemMessage(data: object) {
|
|
@@ -728,7 +798,7 @@ export class Dispatcher {
|
|
|
728
798
|
this.state.status = ClientStatus.Logining;
|
|
729
799
|
this.sendPacket("hello", {
|
|
730
800
|
name: this.state.clientOptions.playerName,
|
|
731
|
-
player_uuid: this.state.
|
|
801
|
+
player_uuid: Buffer.from(this.state.playerUUID!, "hex")
|
|
732
802
|
});
|
|
733
803
|
}
|
|
734
804
|
|
|
@@ -741,9 +811,11 @@ export class Dispatcher {
|
|
|
741
811
|
key: createPublicKey({ key: publicKey, format: 'der', type: 'spki' }),
|
|
742
812
|
padding: crypto_constants.RSA_PKCS1_PADDING
|
|
743
813
|
}, verifyToken);
|
|
814
|
+
const secretBuf = sliceBuffer(encryptedSecret, 1, (buf) => buf.readInt8());
|
|
815
|
+
const token = sliceBuffer(encryptedVerifyToken, 1, (buf) => buf.readInt8());
|
|
744
816
|
this.sendPacket("key", {
|
|
745
|
-
shared_secret: { value:
|
|
746
|
-
verify_token: { value:
|
|
817
|
+
shared_secret: { value: secretBuf, length: secretBuf.length },
|
|
818
|
+
verify_token: { value: token, length: token.length }
|
|
747
819
|
});
|
|
748
820
|
}
|
|
749
821
|
|
|
@@ -771,6 +843,20 @@ export class Dispatcher {
|
|
|
771
843
|
|
|
772
844
|
// Play
|
|
773
845
|
|
|
846
|
+
private sendPlayerSession() {
|
|
847
|
+
const sessionUUID = randomUUIDBytes();
|
|
848
|
+
const expireTime = 15 * 60 * 1000; // 15min in ms
|
|
849
|
+
const expiredAt = Date.now() + expireTime;
|
|
850
|
+
const { publicKey, signature } = this.state.getSignature();
|
|
851
|
+
this.state.sessionID = sessionUUID;
|
|
852
|
+
this.sendPacket("chat_session_update", {
|
|
853
|
+
session_id: sessionUUID,
|
|
854
|
+
expire_at: expiredAt,
|
|
855
|
+
public_key: publicKey,
|
|
856
|
+
signature
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
|
|
774
860
|
private sendConfirmTeleportation(teleportId: number) {
|
|
775
861
|
this.sendPacket("accept_teleportation", {
|
|
776
862
|
teleport_id: teleportId
|
|
@@ -782,4 +868,52 @@ export class Dispatcher {
|
|
|
782
868
|
keep_alive_id: id
|
|
783
869
|
});
|
|
784
870
|
}
|
|
871
|
+
|
|
872
|
+
public sendPlayerPos(
|
|
873
|
+
position: BaseVec3,
|
|
874
|
+
onGround: boolean,
|
|
875
|
+
horizontalCollision: boolean
|
|
876
|
+
) {
|
|
877
|
+
const { x, y, z } = position;
|
|
878
|
+
this.state.enqueuePacket("move_player_pos", {
|
|
879
|
+
x, feet_y: y, z,
|
|
880
|
+
flags: makeMovementFlag(onGround, horizontalCollision)
|
|
881
|
+
});
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
public sendPlayerPosRot(
|
|
885
|
+
position: BaseVec3,
|
|
886
|
+
angle: Angle,
|
|
887
|
+
onGround: boolean,
|
|
888
|
+
horizontalCollision: boolean
|
|
889
|
+
) {
|
|
890
|
+
const { x, y, z } = position,
|
|
891
|
+
{ yaw, pitch } = angle;
|
|
892
|
+
this.state.enqueuePacket("move_player_pos_rot", {
|
|
893
|
+
x, feet_y: y, z,
|
|
894
|
+
yaw, pitch,
|
|
895
|
+
flags: makeMovementFlag(onGround, horizontalCollision)
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
public sendPlayerRot(
|
|
900
|
+
angle: Angle,
|
|
901
|
+
onGround: boolean,
|
|
902
|
+
horizontalCollision: boolean
|
|
903
|
+
) {
|
|
904
|
+
const { yaw, pitch } = angle;
|
|
905
|
+
this.state.enqueuePacket("move_player_rot", {
|
|
906
|
+
yaw, pitch,
|
|
907
|
+
flags: makeMovementFlag(onGround, horizontalCollision)
|
|
908
|
+
});
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
public sendPlayerStatus(
|
|
912
|
+
onGround: boolean,
|
|
913
|
+
horizontalCollision: boolean
|
|
914
|
+
) {
|
|
915
|
+
this.state.enqueuePacket("move_player_status_only", {
|
|
916
|
+
flags: makeMovementFlag(onGround, horizontalCollision)
|
|
917
|
+
});
|
|
918
|
+
}
|
|
785
919
|
}
|
package/src/physics/player.ts
CHANGED
|
@@ -3,25 +3,31 @@ import { Angle, Axis, BaseAxis, BaseVec3, Vec3 } from "./direction";
|
|
|
3
3
|
import { AABB, Shapes, VoxelShape } from "./aabb";
|
|
4
4
|
import { BlockGetter, BlockManager, BlockState } from "../world/block";
|
|
5
5
|
import { clamp, Epsilon, equal, lerp } from "../base/math";
|
|
6
|
-
import { ClientNotReady } from "../base/error";
|
|
6
|
+
import { ClientNotReady, NotImplemented } from "../base/error";
|
|
7
7
|
import { LevelHeightLimit } from "../client/static";
|
|
8
8
|
import { SharedState } from "../world/state";
|
|
9
9
|
import { TypedEmmiter } from "../base/event";
|
|
10
10
|
import { ClientEvents } from "../client/client";
|
|
11
11
|
import { makeMovementFlag } from "../packet/encoder";
|
|
12
12
|
|
|
13
|
-
export enum
|
|
13
|
+
export enum Input {
|
|
14
14
|
Forward = "W",
|
|
15
15
|
Backward = "S",
|
|
16
16
|
Left = "A",
|
|
17
17
|
Right = "D",
|
|
18
|
+
Crouching = "shift",
|
|
19
|
+
Jump = "space",
|
|
20
|
+
Sprint = "cttl"
|
|
18
21
|
}
|
|
19
22
|
|
|
20
|
-
const VectorMovement: Record<
|
|
21
|
-
[
|
|
22
|
-
[
|
|
23
|
-
[
|
|
24
|
-
[
|
|
23
|
+
const VectorMovement: Record<Input, Vec3> = {
|
|
24
|
+
[Input.Forward]: new Vec3(0, 0, 1),
|
|
25
|
+
[Input.Backward]: new Vec3(0, 0, -1),
|
|
26
|
+
[Input.Left]: new Vec3(1, 0, 0),
|
|
27
|
+
[Input.Right]: new Vec3(-1, 0, 0),
|
|
28
|
+
[Input.Crouching]: Vec3.Zero,
|
|
29
|
+
[Input.Jump]: Vec3.Zero,
|
|
30
|
+
[Input.Sprint]: Vec3.Zero,
|
|
25
31
|
};
|
|
26
32
|
|
|
27
33
|
/**
|
|
@@ -42,12 +48,11 @@ export class Player {
|
|
|
42
48
|
|
|
43
49
|
// Moving state
|
|
44
50
|
// TODO: need is sprinting
|
|
45
|
-
private heldInputs: Set<
|
|
46
|
-
private isCrouncing: boolean = false;
|
|
47
|
-
private isJumping: boolean = false;
|
|
51
|
+
private heldInputs: Set<Input> = new Set();
|
|
48
52
|
private lastPos: BaseVec3 | null = null;
|
|
49
53
|
private lastAngle: Angle | null = null;
|
|
50
54
|
private positionReminder: number = 0;
|
|
55
|
+
private noJumpDelay: number = 0;
|
|
51
56
|
|
|
52
57
|
// Collision
|
|
53
58
|
private horizontalCollision: boolean = false;
|
|
@@ -67,9 +72,35 @@ export class Player {
|
|
|
67
72
|
this.horizontalCollision = false;
|
|
68
73
|
this.fallDistance = 0;
|
|
69
74
|
this.deltaMovement = new Vec3(this.state.player!.velocity);
|
|
75
|
+
this.setInitialVal();
|
|
70
76
|
});
|
|
71
77
|
}
|
|
72
78
|
|
|
79
|
+
|
|
80
|
+
public sendPlayerPos: (
|
|
81
|
+
position: BaseVec3,
|
|
82
|
+
onGround: boolean,
|
|
83
|
+
horizontalCollision: boolean
|
|
84
|
+
) => void = () => { throw new NotImplemented(); };
|
|
85
|
+
|
|
86
|
+
public sendPlayerPosRot: (
|
|
87
|
+
position: BaseVec3,
|
|
88
|
+
angle: Angle,
|
|
89
|
+
onGround: boolean,
|
|
90
|
+
horizontalCollision: boolean
|
|
91
|
+
) => void = () => { throw new NotImplemented(); };
|
|
92
|
+
|
|
93
|
+
public sendPlayerRot: (
|
|
94
|
+
angle: Angle,
|
|
95
|
+
onGround: boolean,
|
|
96
|
+
horizontalCollision: boolean
|
|
97
|
+
) => void = () => { throw new NotImplemented(); };
|
|
98
|
+
|
|
99
|
+
public sendPlayerStatus: (
|
|
100
|
+
onGround: boolean,
|
|
101
|
+
horizontalCollision: boolean
|
|
102
|
+
) => void = () => { throw new NotImplemented(); };
|
|
103
|
+
|
|
73
104
|
/**
|
|
74
105
|
* Set initial value
|
|
75
106
|
*/
|
|
@@ -85,8 +116,6 @@ export class Player {
|
|
|
85
116
|
this.deltaMovement = Vec3.Zero;
|
|
86
117
|
this.supportBlockPos = null;
|
|
87
118
|
this.heldInputs = new Set();
|
|
88
|
-
this.isCrouncing = false;
|
|
89
|
-
this.isJumping = false;
|
|
90
119
|
this.lastPos = null;
|
|
91
120
|
this.lastAngle = null;
|
|
92
121
|
this.positionReminder = 0;
|
|
@@ -101,31 +130,19 @@ export class Player {
|
|
|
101
130
|
/**
|
|
102
131
|
* Press and hold a key
|
|
103
132
|
*/
|
|
104
|
-
public input(input:
|
|
133
|
+
public input(input: Input) { this.heldInputs.add(input); }
|
|
105
134
|
/**
|
|
106
135
|
* Release a key
|
|
107
136
|
*/
|
|
108
|
-
public releaseInput(input:
|
|
137
|
+
public releaseInput(input: Input) { this.heldInputs.delete(input); }
|
|
109
138
|
/**
|
|
110
139
|
* Release all key
|
|
111
140
|
*/
|
|
112
141
|
public releaseAllInputs() { this.heldInputs.clear(); }
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Release shift
|
|
119
|
-
*/
|
|
120
|
-
public releaseShift() { this.isCrouncing = false; }
|
|
121
|
-
/**
|
|
122
|
-
* Press and hold space. Do nothing for now
|
|
123
|
-
*/
|
|
124
|
-
public pressSpace() { this.isJumping = true; }
|
|
125
|
-
/**
|
|
126
|
-
* Release shift
|
|
127
|
-
*/
|
|
128
|
-
public releaseSpace() { this.isJumping = false; }
|
|
142
|
+
|
|
143
|
+
private get isCrouncing() { return this.heldInputs.has(Input.Crouching); };
|
|
144
|
+
private get isJumping() { return this.heldInputs.has(Input.Jump); };
|
|
145
|
+
private get isSprinting() { return this.heldInputs.has(Input.Sprint); }
|
|
129
146
|
|
|
130
147
|
private getInputVector(): Vec3 {
|
|
131
148
|
let vec = Vec3.Zero;
|
|
@@ -141,12 +158,19 @@ export class Player {
|
|
|
141
158
|
}
|
|
142
159
|
private setPos(pos: Vec3) {
|
|
143
160
|
this.state.checkReady();
|
|
144
|
-
this.state.player!.position = pos.copyBase();
|
|
161
|
+
this.state.enqueueMutation((state) => state.player!.position = pos.copyBase());
|
|
145
162
|
}
|
|
146
163
|
private getAngle() {
|
|
147
164
|
this.state.checkReady();
|
|
148
165
|
return this.state.player!.angle;
|
|
149
166
|
}
|
|
167
|
+
public setAngle(yaw: number, pitch: number) {
|
|
168
|
+
this.state.checkReady();
|
|
169
|
+
this.state.enqueueMutation((state) => state.player!.angle = {
|
|
170
|
+
yaw: yaw % 360,
|
|
171
|
+
pitch: pitch % 360
|
|
172
|
+
});
|
|
173
|
+
}
|
|
150
174
|
private getDimension() {
|
|
151
175
|
this.state.checkReady();
|
|
152
176
|
return this.state.player!.dimension;
|
|
@@ -241,28 +265,16 @@ export class Player {
|
|
|
241
265
|
const move = new Vec3(deltaX, deltaY, deltaZ).lengthSqr() > (2e-4 * 2e-4) || this.positionReminder > 20,
|
|
242
266
|
rot = deltaXRot !== 0 || deltaYRot !== 0;
|
|
243
267
|
if (move && rot)
|
|
244
|
-
this.
|
|
245
|
-
x, feet_y: y, z,
|
|
246
|
-
yaw, pitch,
|
|
247
|
-
flags: makeMovementFlag(this.onGround, this.horizontalCollision)
|
|
248
|
-
});
|
|
268
|
+
this.sendPlayerPosRot(this.getPos(), this.getAngle(), this.onGround, this.horizontalCollision);
|
|
249
269
|
else if (move)
|
|
250
|
-
this.
|
|
251
|
-
x, feet_y: y, z,
|
|
252
|
-
flags: makeMovementFlag(this.onGround, this.horizontalCollision)
|
|
253
|
-
});
|
|
270
|
+
this.sendPlayerPos(this.getPos(), this.onGround, this.horizontalCollision);
|
|
254
271
|
else if (rot)
|
|
255
|
-
this.
|
|
256
|
-
yaw, pitch,
|
|
257
|
-
flags: makeMovementFlag(this.onGround, this.horizontalCollision)
|
|
258
|
-
});
|
|
272
|
+
this.sendPlayerRot(this.getAngle(), this.onGround, this.horizontalCollision);
|
|
259
273
|
else if (
|
|
260
274
|
this.onGround !== this.lastOnGround ||
|
|
261
275
|
this.horizontalCollision !== this.lastHorizontalCollision
|
|
262
276
|
)
|
|
263
|
-
this.
|
|
264
|
-
flags: makeMovementFlag(this.onGround, this.horizontalCollision)
|
|
265
|
-
});
|
|
277
|
+
this.sendPlayerStatus(this.onGround, this.horizontalCollision);
|
|
266
278
|
|
|
267
279
|
if (move) {
|
|
268
280
|
this.lastPos = this.getPos();
|
|
@@ -276,6 +288,10 @@ export class Player {
|
|
|
276
288
|
}
|
|
277
289
|
|
|
278
290
|
private aiStep() {
|
|
291
|
+
if (this.noJumpDelay > 0) {
|
|
292
|
+
this.noJumpDelay--;
|
|
293
|
+
}
|
|
294
|
+
|
|
279
295
|
// eslint-disable-next-line prefer-const
|
|
280
296
|
let { x, y, z } = this.deltaMovement;
|
|
281
297
|
if (this.deltaMovement.horizontalDistanceSqr() < 9e-6) {
|
|
@@ -284,13 +300,23 @@ export class Player {
|
|
|
284
300
|
}
|
|
285
301
|
this.setDeltaMovement(x, y, z);
|
|
286
302
|
|
|
287
|
-
if (this.isJumping) {
|
|
303
|
+
if (this.isJumping && this.noJumpDelay === 0) {
|
|
288
304
|
const jumpPower = this.getJumpFactor();
|
|
289
305
|
|
|
290
306
|
if (jumpPower > 1e-5) {
|
|
291
307
|
const { x: dx, y: dy, z: dz } = this.deltaMovement;
|
|
292
308
|
this.setDeltaMovement(dx, Math.max(dy, jumpPower), dz);
|
|
309
|
+
if (this.isSprinting) {
|
|
310
|
+
const yawInRad = this.getAngle().yaw * (Math.PI / 180.0);
|
|
311
|
+
this.setDeltaMovement(
|
|
312
|
+
this.deltaMovement.add(
|
|
313
|
+
new Vec3(-Math.sin(yawInRad) * 0.2, 0.0, Math.cos(yawInRad) * 0.2)
|
|
314
|
+
)
|
|
315
|
+
);
|
|
316
|
+
}
|
|
293
317
|
}
|
|
318
|
+
|
|
319
|
+
this.noJumpDelay = 15; // Original value to 10
|
|
294
320
|
}
|
|
295
321
|
// TODO: Handle in fluid
|
|
296
322
|
|
|
@@ -312,14 +338,15 @@ export class Player {
|
|
|
312
338
|
const { x, y, z } = this.getPos();
|
|
313
339
|
const inBlock = this.blocks.at(x, y, z)!;
|
|
314
340
|
const belowBlock = this.blocks.at(x, y - 0.5, z)!;
|
|
315
|
-
|
|
341
|
+
// 0.42F is the default jump strength, which will apply for player here
|
|
342
|
+
return (inBlock.owner.getJumpFactor() === 1 ? belowBlock.owner.getJumpFactor() : inBlock.owner.getJumpFactor()) * 0.42;
|
|
316
343
|
}
|
|
317
344
|
|
|
318
345
|
/*
|
|
319
346
|
* Invoking flow
|
|
320
347
|
*
|
|
321
348
|
* |-> travelInAir ---|
|
|
322
|
-
* travel -|-> travelInFluid
|
|
349
|
+
* travel -|-> travelInFluid -+--> move -> colision
|
|
323
350
|
* |-> flying? -------|
|
|
324
351
|
*/
|
|
325
352
|
|
|
@@ -334,8 +361,8 @@ export class Player {
|
|
|
334
361
|
private travelInAir(input: Vec3) {
|
|
335
362
|
const posBelow = this.getOnPos(0.50001);
|
|
336
363
|
if (!this.blocks.hasChunkAt(posBelow))
|
|
337
|
-
return; //
|
|
338
|
-
//
|
|
364
|
+
return; // console.log(`no chunk at ${Math.floor(posBelow.x / 16)} ${Math.floor(posBelow.y / 16)} ${Math.floor(posBelow.z / 16)}`);
|
|
365
|
+
// console.log('posBelow', posBelow, 'block', this.blocks.at(posBelow)?.owner.getType());
|
|
339
366
|
const blockFriction = this.onGround ? this.blocks.at(posBelow)!.owner.getFriction() : 1;
|
|
340
367
|
const movement = this.handleFrictionAndCalculateMovement(input, blockFriction);
|
|
341
368
|
let movementY = movement.y;
|
|
@@ -389,7 +416,7 @@ export class Player {
|
|
|
389
416
|
private getSpeed() {
|
|
390
417
|
// return the base for now
|
|
391
418
|
// TODO: Handle other cases: sprinting, speed-enchanted gear and potion
|
|
392
|
-
return 0.1;
|
|
419
|
+
return this.isSprinting ? 0.13 : 0.1;
|
|
393
420
|
}
|
|
394
421
|
|
|
395
422
|
private moveRelative(input: Vec3, speed: number) {
|
|
@@ -433,14 +460,12 @@ export class Player {
|
|
|
433
460
|
to: checkTo,
|
|
434
461
|
getBlockState: (position) => this.blocks.at(position)!.shape
|
|
435
462
|
});
|
|
436
|
-
if (hitResult?.miss !== false)
|
|
463
|
+
if (hitResult?.miss !== false)
|
|
437
464
|
this.fallDistance = 0;
|
|
438
|
-
}
|
|
439
465
|
}
|
|
440
466
|
|
|
441
467
|
const pos = this.getPos();
|
|
442
468
|
const newPosition = pos.add(movement);
|
|
443
|
-
// addMovementThisTick(new Movement(pos, newPosition, delta2));
|
|
444
469
|
this.setPos(newPosition);
|
|
445
470
|
}
|
|
446
471
|
|
|
@@ -600,8 +625,8 @@ export class Player {
|
|
|
600
625
|
}
|
|
601
626
|
}
|
|
602
627
|
|
|
603
|
-
//
|
|
604
|
-
//
|
|
628
|
+
// console.log('expandedBB', expandedBB, 'colliderCount', colliders.length);
|
|
629
|
+
// console.log('movement.y', movement.y, 'movementStep.y', movementStep.y);
|
|
605
630
|
|
|
606
631
|
return movementStep;
|
|
607
632
|
}
|