Implement multiplayer persistence and vanilla lighting

This commit is contained in:
Master290 2026-07-21 09:21:44 +03:00
parent 8f7cacf9d9
commit cae06eb97e
47 changed files with 3784 additions and 465 deletions

View file

@ -70,6 +70,15 @@ func (r *Reader) Uint16() (uint16, error) {
return binary.BigEndian.Uint16(b), nil
}
// Int32 reads a big-endian signed int.
func (r *Reader) Int32() (int32, error) {
b, err := r.readN(4)
if err != nil {
return 0, err
}
return int32(binary.BigEndian.Uint32(b)), nil
}
// Int64 reads a big-endian signed long.
func (r *Reader) Int64() (int64, error) {
b, err := r.readN(8)
@ -119,9 +128,9 @@ func (r *Reader) Position() (x, y, z int, err error) {
if err != nil {
return 0, 0, 0, err
}
x = int(v >> 38) // top 26 bits, sign-extended
y = int(v << 52 >> 52) // low 12 bits, sign-extended
z = int(v << 26 >> 38) // middle 26 bits, sign-extended
x = int(v >> 38) // top 26 bits, sign-extended
y = int(v << 52 >> 52) // low 12 bits, sign-extended
z = int(v << 26 >> 38) // middle 26 bits, sign-extended
return x, y, z, nil
}

View file

@ -31,11 +31,11 @@ const (
// Login, clientbound.
const (
LoginDisconnectID = 0x00
EncryptionRequest = 0x01
LoginSuccessID = 0x02
SetCompressionID = 0x03
LoginPluginReq = 0x04
LoginDisconnectID = 0x00
EncryptionRequest = 0x01
LoginSuccessID = 0x02
SetCompressionID = 0x03
LoginPluginReq = 0x04
CookieRequestLogin = 0x05
)
@ -53,12 +53,12 @@ const (
// Configuration, clientbound.
const (
ConfigCookieRequest = 0x00
ConfigCustomPayloadCB = 0x01
ConfigDisconnect = 0x02
ConfigFinishClientbound = 0x03
ConfigKeepAliveCB = 0x04
ConfigPing = 0x05
ConfigCookieRequest = 0x00
ConfigCustomPayloadCB = 0x01
ConfigDisconnect = 0x02
ConfigFinishClientbound = 0x03
ConfigKeepAliveCB = 0x04
ConfigPing = 0x05
ConfigRegistryData = 0x07
ConfigUpdateEnabledFeatures = 0x0c
ConfigUpdateTags = 0x0d
@ -74,42 +74,46 @@ const (
PlayDefaultSpawnPos = 0x61
PlayChunkCacheCenter = 0x5e
PlayLevelChunk = 0x2d
PlayForgetLevelChunk = 0x25
PlayAbilities = 0x40
PlaySetHeldSlot = 0x69
PlayDisconnect = 0x20
PlayBlockUpdate = 0x08
PlayBlockChangedAck = 0x04
PlaySystemChat = 0x79
PlayLightUpdate = 0x30
PlayPlayerInfoRemove = 0x45
PlayPlayerInfoUpdate = 0x46
// Entity packets
PlayAddEntity = 0x01
PlayTeleportEntity = 0x7D
PlayTeleportEntity = 0x7d
PlayMoveEntityPos = 0x35
PlayMoveEntityPosRot = 0x36
PlayMoveEntityRot = 0x38
PlaySetEntityMotion = 0x65
PlayRemoveEntities = 0x4D
PlayRemoveEntities = 0x4d
PlaySetEntityData = 0x63
PlaySetEquipment = 0x66
)
// Play, serverbound (protocol 775).
const (
PlayAcceptTeleport = 0x00
PlayKeepAliveServer = 0x1c
PlayClientTickEnd = 0x0d
PlayClientInformation = 0x0e
PlayCustomPayload = 0x16
PlayMovePos = 0x1e
PlayMovePosRot = 0x1f
PlayMoveRot = 0x20
PlayMoveStatusOnly = 0x21
PlayPlayerLoaded = 0x2c
PlayPlayerAction = 0x29
PlayUseItemOn = 0x42
PlaySetCreativeSlot = 0x38
PlaySetCarriedItem = 0x35
PlayChatMessage = 0x09
PlayAcceptTeleport = 0x00
PlayKeepAliveServer = 0x1c
PlayClientTickEnd = 0x0d
PlayClientInformation = 0x0e
PlayCustomPayload = 0x16
PlayMovePos = 0x1e
PlayMovePosRot = 0x1f
PlayMoveRot = 0x20
PlayMoveStatusOnly = 0x21
PlayPlayerLoaded = 0x2c
PlayPlayerAction = 0x29
PlayUseItemOn = 0x42
PlaySetCreativeSlot = 0x38
PlaySetCarriedItem = 0x35
PlayChatMessage = 0x09
)
// GameEvent sub-IDs carried by the clientbound game_event packet.

View file

@ -0,0 +1,117 @@
package protocol
import "testing"
func TestPlayPacketIDsProtocol775(t *testing.T) {
clientbound := map[string]int32{
"PlayAddEntity": PlayAddEntity,
"PlayBlockChangedAck": PlayBlockChangedAck,
"PlayBlockUpdate": PlayBlockUpdate,
"PlayDisconnect": PlayDisconnect,
"PlayForgetLevelChunk": PlayForgetLevelChunk,
"PlayGameEvent": PlayGameEvent,
"PlayKeepAliveCB": PlayKeepAliveCB,
"PlayLevelChunk": PlayLevelChunk,
"PlayLogin": PlayLogin,
"PlayMoveEntityPos": PlayMoveEntityPos,
"PlayMoveEntityPosRot": PlayMoveEntityPosRot,
"PlayMoveEntityRot": PlayMoveEntityRot,
"PlayAbilities": PlayAbilities,
"PlayPlayerPosition": PlayPlayerPosition,
"PlayRemoveEntities": PlayRemoveEntities,
"PlayChunkCacheCenter": PlayChunkCacheCenter,
"PlayDefaultSpawnPos": PlayDefaultSpawnPos,
"PlaySetEntityData": PlaySetEntityData,
"PlaySetEntityMotion": PlaySetEntityMotion,
"PlaySetEquipment": PlaySetEquipment,
"PlaySetHeldSlot": PlaySetHeldSlot,
"PlaySystemChat": PlaySystemChat,
"PlayLightUpdate": PlayLightUpdate,
"PlayPlayerInfoRemove": PlayPlayerInfoRemove,
"PlayPlayerInfoUpdate": PlayPlayerInfoUpdate,
"PlayTeleportEntity": PlayTeleportEntity,
}
wantClientbound := map[string]int32{
"PlayAddEntity": 0x01,
"PlayBlockChangedAck": 0x04,
"PlayBlockUpdate": 0x08,
"PlayDisconnect": 0x20,
"PlayForgetLevelChunk": 0x25,
"PlayGameEvent": 0x26,
"PlayKeepAliveCB": 0x2c,
"PlayLevelChunk": 0x2d,
"PlayLogin": 0x31,
"PlayMoveEntityPos": 0x35,
"PlayMoveEntityPosRot": 0x36,
"PlayMoveEntityRot": 0x38,
"PlayAbilities": 0x40,
"PlayPlayerPosition": 0x48,
"PlayRemoveEntities": 0x4d,
"PlayChunkCacheCenter": 0x5e,
"PlayDefaultSpawnPos": 0x61,
"PlaySetEntityData": 0x63,
"PlaySetEntityMotion": 0x65,
"PlaySetEquipment": 0x66,
"PlaySetHeldSlot": 0x69,
"PlaySystemChat": 0x79,
"PlayLightUpdate": 0x30,
"PlayPlayerInfoRemove": 0x45,
"PlayPlayerInfoUpdate": 0x46,
"PlayTeleportEntity": 0x7d,
}
assertIDs(t, "clientbound", clientbound, wantClientbound)
serverbound := map[string]int32{
"PlayAcceptTeleport": PlayAcceptTeleport,
"PlayKeepAliveServer": PlayKeepAliveServer,
"PlayClientTickEnd": PlayClientTickEnd,
"PlayClientInformation": PlayClientInformation,
"PlayCustomPayload": PlayCustomPayload,
"PlayMovePos": PlayMovePos,
"PlayMovePosRot": PlayMovePosRot,
"PlayMoveRot": PlayMoveRot,
"PlayMoveStatusOnly": PlayMoveStatusOnly,
"PlayPlayerLoaded": PlayPlayerLoaded,
"PlayPlayerAction": PlayPlayerAction,
"PlayUseItemOn": PlayUseItemOn,
"PlaySetCreativeSlot": PlaySetCreativeSlot,
"PlaySetCarriedItem": PlaySetCarriedItem,
"PlayChatMessage": PlayChatMessage,
}
wantServerbound := map[string]int32{
"PlayAcceptTeleport": 0x00,
"PlayKeepAliveServer": 0x1c,
"PlayClientTickEnd": 0x0d,
"PlayClientInformation": 0x0e,
"PlayCustomPayload": 0x16,
"PlayMovePos": 0x1e,
"PlayMovePosRot": 0x1f,
"PlayMoveRot": 0x20,
"PlayMoveStatusOnly": 0x21,
"PlayPlayerLoaded": 0x2c,
"PlayPlayerAction": 0x29,
"PlayUseItemOn": 0x42,
"PlaySetCreativeSlot": 0x38,
"PlaySetCarriedItem": 0x35,
"PlayChatMessage": 0x09,
}
assertIDs(t, "serverbound", serverbound, wantServerbound)
}
func assertIDs(t *testing.T, direction string, got, want map[string]int32) {
t.Helper()
seen := make(map[int32]string, len(got))
for name, gotID := range got {
wantID, ok := want[name]
if !ok {
t.Fatalf("%s %s missing expected ID", direction, name)
}
if gotID != wantID {
t.Fatalf("%s %s = %#x, want %#x", direction, name, gotID, wantID)
}
if previous, ok := seen[gotID]; ok {
t.Fatalf("%s duplicate packet ID %#x: %s and %s", direction, gotID, previous, name)
}
seen[gotID] = name
}
}