PrismarineJS / node-minecraft-protocol

Parse and serialize minecraft packets, plus authentication and encryption.
https://prismarinejs.github.io/node-minecraft-protocol/
BSD 3-Clause "New" or "Revised" License
1.23k stars 239 forks source link

Example doesn't work on 1.18.2 #977

Open x1msoc opened 2 years ago

x1msoc commented 2 years ago

[ ] The FAQ doesn't contain a resolution to my issue

Versions

Detailed description of a problem

Example code doesn't work on 1.18.2

Current code

const mc = require('minecraft-protocol')
const Chunk = require('prismarine-chunk')('1.18.2')
const Vec3 = require('vec3')
const server = mc.createServer({
  'online-mode': true,
  encryption: true,
  host: '0.0.0.0',
  port: 25565,
  version: '1.18.2'
})
const mcData = require('minecraft-data')(server.version)
const loginPacket = mcData.loginPacket
const chunk = new Chunk()

for (let x = 0; x < 16; x++) {
  for (let z = 0; z < 16; z++) {
    chunk.setBlockType(new Vec3(x, 100, z), mcData.blocksByName.grass_block.id)
    chunk.setBlockData(new Vec3(x, 100, z), 1)
    for (let y = 0; y < 256; y++) {
      chunk.setSkyLight(new Vec3(x, y, z), 15)
    }
  }
}

server.on('login', function (client) {
  client.write('login', {
    entityId: client.id,
    isHardcore: false,
    gameMode: 0,
    previousGameMode: 1,
    worldNames: loginPacket.worldNames,
    dimensionCodec: loginPacket.dimensionCodec,
    dimension: loginPacket.dimension,
    worldName: 'minecraft:overworld',
    hashedSeed: [0, 0],
    maxPlayers: server.maxPlayers,
    viewDistance: 10,
    reducedDebugInfo: false,
    enableRespawnScreen: true,
    isDebug: false,
    isFlat: false
  })
  client.write('map_chunk', {
    x: 0,
    z: 0,
    groundUp: true,
    biomes: chunk.dumpBiomes !== undefined ? chunk.dumpBiomes() : undefined,
    heightmaps: {
      type: 'compound',
      name: '',
      value: {} // Client will accept fake heightmap
    },
    bitMap: chunk.getMask(),
    chunkData: chunk.dump(),
    blockEntities: []
  })
  client.write('position', {
    x: 15,
    y: 101,
    z: 15,
    yaw: 137,
    pitch: 0,
    flags: 0x00
  })
})

Expected behavior

When joining the server, blocks / chunk will be visible

Additional context

None

extremeheat commented 2 years ago

Elaborate on "doesn't work"? Do you get kicked? No chunks? Not enough information here

ignGeri commented 2 years ago

Elaborate on "doesn't work"? Do you get kicked? No chunks? Not enough information here

I think they meant that the player gets kicked for the reason Internal Exception: io.netty.handler.codec.DecoderException: com.google.gson.JsonSyntaxException: Expected text to be a string, was an object ({"fi...t"}) and with a console error like:

Error: TypeError: Serialization error for play.toClient : SizeOf error for undefined : Cannot read properties of undefined (reading 'length')
    at eval (eval at compile (/home/username/Desktop/Folder/node_modules/protodef/src/compiler.js:258:12), <anonymous>:1151:39)
    at Object.packet_map_chunk (eval at compile (/home/username/Desktop/Folder/node_modules/protodef/src/compiler.js:258:12), <anonymous>:1156:9)
    at eval (eval at compile (/home/username/Desktop/Folder/node_modules/protodef/src/compiler.js:258:12), <anonymous>:2529:58)
    at packet (eval at compile (/home/username/Desktop/Folder/node_modules/protodef/src/compiler.js:258:12), <anonymous>:2600:9)
    at CompiledProtodef.sizeOf (/home/username/Desktop/Folder/node_modules/protodef/src/compiler.js:89:14)
    at e.message (/home/username/Desktop/Folder/node_modules/protodef/src/compiler.js:96:40)
    at tryCatch (/home/username/Desktop/Folder/node_modules/protodef/src/utils.js:50:16)
    at CompiledProtodef.createPacketBuffer (/home/username/Desktop/Folder/node_modules/protodef/src/compiler.js:96:20)
    at Serializer.createPacketBuffer (/home/username/Desktop/Folder/node_modules/protodef/src/serializer.js:12:23)
    at Serializer._transform (/home/username/Desktop/Folder/node_modules/protodef/src/serializer.js:18:18) {
  field: 'play.toClient'
}
ItsVaidas commented 1 year ago

Will this be fixed?

IuCC123 commented 1 year ago

Still havent found a fix. Great

majusss commented 1 year ago

Same on 1.19.4

lkwilson commented 1 year ago

https://github.com/PrismarineJS/node-minecraft-protocol/issues/1249

Related?

h5mcbox commented 1 year ago

This is my solution based on 1.19.2:

const mc = require('minecraft-protocol')
const Chunk = require('prismarine-chunk')('1.18.2')
const Vec3 = require('vec3')
const server = mc.createServer({
  'online-mode': true,
  encryption: true,
  host: '0.0.0.0',
  port: 25565,
  version: '1.18.2'
})
const mcData = require('minecraft-data')(server.version)
const loginPacket = mcData.loginPacket;
const chunk = new Chunk({
    minY:-64,
    worldHeight:384
});

for (let x = 0; x < 16; x++) {
  for (let z = 0; z < 16; z++) {
    chunk.setBlockType(new Vec3(x, 100, z), mcData.blocksByName.grass_block.id)
    chunk.setBlockData(new Vec3(x, 100, z), 1)
    for (let y = 0; y < 256; y++) {
      chunk.setSkyLight(new Vec3(x, y, z), 15)
    }
  }
}

let skyLight = [],blockLight = [];
chunk.skyLightSections.forEach(e=>e!==null&&skyLight.push(new Uint8Array(e.data.buffer)));
chunk.blockLightSections.forEach(e=>e!==null&&blockLight.push(new Uint8Array(e.data.buffer)));

server.on('login', function (client) {
  client.write('login', {
    entityId: client.id,
    isHardcore: false,
    gameMode: 0,
    previousGameMode: 1,
    worldNames: loginPacket.worldNames,
    dimensionCodec: loginPacket.dimensionCodec,
    dimension: loginPacket.dimension,
    worldName: 'minecraft:overworld',
    hashedSeed: [0, 0],
    maxPlayers: server.maxPlayers,
    viewDistance: 10,
    reducedDebugInfo: false,
    enableRespawnScreen: true,
    isDebug: false,
    isFlat: false
  })
  client.write('map_chunk', {
    x: 0,
    z: 0,
    groundUp: true,
    biomes: chunk.dumpBiomes&&chunk.dumpBiomes(),
    heightmaps: {
      type: 'compound',
      name: '',
      value: {} // Client will accept fake heightmap
    },
    chunkData: chunk.dump(),
    blockEntities: [],
    trustEdges:true,
    blockLightMask:chunk.blockLightMask.toLongArray(),
    emptyBlockLightMask:chunk.emptyBlockLightMask.toLongArray(),
    skyLightMask:chunk.skyLightMask.toLongArray(),
    emptySkyLightMask:chunk.emptySkyLightMask.toLongArray(),
    skyLight,
    blockLight
  })
  client.write('position', {
    x: 15,
    y: 101,
    z: 15,
    yaw: 137,
    pitch: 0,
    flags: 0x00
  })
})

References:

Since minecraft 1.18,the max world height is changed to 384,and the minimum Y coordinate is changed to -64. A chunk section holds 16x16x16 Blocks.So there is (384/16=24) chunk sections in a chunk colnum(16x384x16). In this situation,we have to set the chunk height from 256(Default in 1.16.5) to 384(1.18.2) and adjust minY in chunk options(Line 13).

packet map_chunk is defined in protocol.json(see reference) as "packet_map_chunk". It defines ["trustEdges","skyLightMask","emptyBlockLightMask","blockLightMask","emptySkyLightMask","skyLight","blockLight"] in the packet.But x1msoc's code doesn't contain it.I made a patch in Line 28 and Line 62.

Then the code works. image

BasToTheMax commented 1 year ago

The login-packet for 1.19 and newer is invalid

h5mcbox commented 1 year ago

The login-packet for 1.19 and newer is invalid


Update 1:add version and NBT handling

Add

const nbt = require('prismarine-nbt');
const w = nbt.comp({
  piglin_safe: nbt.byte(0),
  natural: nbt.byte(1),
  ambient_light: nbt.float(0),
  infiniburn: nbt.string('minecraft:infiniburn_overworld'),
  respawn_anchor_works: nbt.byte(0),
  has_skylight: nbt.byte(1),
  bed_works: nbt.byte(1),
  has_raids: nbt.byte(1),
  name: nbt.string('minecraft:overworld'),
  logical_height: nbt.int(256),
  shrunk: nbt.byte(0),
  ultrawarm: nbt.byte(0),
  has_ceiling: nbt.byte(0)
})
const mcData = require('minecraft-data')(server.version) //or specify it manually

const version = mcData.version
const loginPacket = (client, server) => {
    return {
      // 1.7
      entityId: client.id,
      gameMode: 1,
      dimension: (version.version >= 735 ? mcData.loginPacket.dimension : 0),
      difficulty: 2,
      maxPlayers: server.maxPlayers,
      levelType: 'default',
      // 1.8
      reducedDebugInfo: (version.version >= 735 ? false : 0),
      // 1.14
      // removes `difficulty`
      viewDistance: 10,
      // 1.15
      hashedSeed: [0, 0],
      enableRespawnScreen: true,
      // 1.16
      // removed levelType
      previousGameMode: version.version >= 755 ? 0 : 255,
      worldNames: ['minecraft:overworld'],
      dimensionCodec: version.version >= 755 ? mcData.loginPacket.dimensionCodec : (version.version >= 735 ? mcData.loginPacket.dimension : { name: '', type: 'compound', value: { dimension: { type: 'list', value: { type: 'compound', value: [w] } } } }),
      worldName: 'minecraft:overworld',
      isDebug: false,
      isFlat: false,
      // 1.16.2
      isHardcore: false,
      // 1.18
      simulationDistance: 10,
      // 1.19
      // removed `dimension`
      // removed `dimensionCodec`
      registryCodec: {
        type: 'compound',
        name: '',
        value: {}
      },
      worldType: 'minecraft:overworld',
      death: undefined
      // more to be added
    }
}

before client.write("login",...)

Then replace client.write("login",...) to client.write("login",loginPacket(client,server))

You can get full code from the reference. Reference:test/serverTest.js

BasToTheMax commented 1 year ago

The login-packet for 1.19 and newer is invalid

Just add

const loginPacket = (client, server) => {
    return {
      // 1.7
      entityId: client.id,
      gameMode: 1,
      dimension: (version.version >= 735 ? mcData.loginPacket.dimension : 0),
      difficulty: 2,
      maxPlayers: server.maxPlayers,
      levelType: 'default',
      // 1.8
      reducedDebugInfo: (version.version >= 735 ? false : 0),
      // 1.14
      // removes `difficulty`
      viewDistance: 10,
      // 1.15
      hashedSeed: [0, 0],
      enableRespawnScreen: true,
      // 1.16
      // removed levelType
      previousGameMode: version.version >= 755 ? 0 : 255,
      worldNames: ['minecraft:overworld'],
      dimensionCodec: version.version >= 755 ? mcData.loginPacket.dimensionCodec : (version.version >= 735 ? mcData.loginPacket.dimension : { name: '', type: 'compound', value: { dimension: { type: 'list', value: { type: 'compound', value: [w] } } } }),
      worldName: 'minecraft:overworld',
      isDebug: false,
      isFlat: false,
      // 1.16.2
      isHardcore: false,
      // 1.18
      simulationDistance: 10,
      // 1.19
      // removed `dimension`
      // removed `dimensionCodec`
      registryCodec: {
        type: 'compound',
        name: '',
        value: {}
      },
      worldType: 'minecraft:overworld',
      death: undefined
      // more to be added
    }
}

before client.write("login",...)

Then replace client.write("login",...) to client.write("login",loginPacket(client,server))

Reference:test/serverTest.js

Thanks. Will try that :D