Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Forge client support (support integrating with node-minecraft-protocol-forge) #326

Merged
merged 50 commits into from
Feb 7, 2016

Conversation

deathcap
Copy link
Member

Add client-side #114 Forge Support

  • Send tagged hostname in set_protocol packet
  • Receive server channel registrations (FML|HS, FML, FML|MP, FML, FORGE)
  • Handle the FML|HS ServerHello with the player's dimension
  • Register client plugin channels (FML|HS, FML, FML|MP, FML, FORGE)
  • Send the FML|HS ClientHello
  • Send client modList
  • Receive server modList
  • Check client/server mod compatibility - emits forgeMods event for app to check
  • Send HandshakeAck
  • Receive RegistryData packet(s) until hasMore=false
  • Send/receive final HandshakeAcks and CompleteHandshake
  • Refactor discriminator byte / 'FMLIndexedMessageToMessageCodec' to use a mapper
  • Refactor discriminator byte / 'FMLIndexedMessageToMessageCodec' parsing to use one big switch
  • Move FML|HS handshake to src/fml.js, support and document forge/forgeMods opts, event
  • Refactor into separate module?
  • Update for Modularization attempt of createClient() #333 Modularization attempt of createClient()
  • Move tagHost=\0FML\0 to be installed by src/client/forgeHandshake.js, not the client example

Not included, out of scope for this PR:

  • Fully parse the RegistryData packets and emit registry events - would be nice but only needed for advanced usage, can come later
  • FORGE plugin channel (observed in packet logging, sends lava and water)
  • FML and FML|MP plugin channels (registered, but not seen that they are used at least at login)
  • Server-side Forge support Forge Support #114

@deathcap
Copy link
Member Author

Current status (testing against server: forge-1.8.9-11.15.0.1715 + mods/ironchest-1.8.9-6.0.121.768.jar):

node-minecraft-protocol $ NODE_DEBUG=mc-proto node examples/client_forge/client_forge.js localhost 25565
MC-PROTO: 19450 writing packet handshaking.set_protocol
MC-PROTO: 19450 { protocolVersion: 47,
  serverHost: 'localhost\u0000FML\u0000',
  serverPort: 25565,
  nextState: 2 }
MC-PROTO: 19450 writing packet login.login_start
MC-PROTO: 19450 { username: 'echo' }
connected
MC-PROTO: 19450 read packet login.compress
MC-PROTO: 19450 { threshold: 256 }
MC-PROTO: 19450 read packet login.success
MC-PROTO: 19450 { uuid: 'c39fa4c6-85a3-3582-8167-db3805dbe851',
  username: 'echo' }
MC-PROTO: 19450 read packet play.custom_payload
MC-PROTO: 19450 { channel: 'REGISTER',
  data: <Buffer 46 4d 4c 7c 48 53 00 46 4d 4c 00 46 4d 4c 7c 4d 50 00 46 4d 4c 00 46 4f 52 47 45> }
^ this buffer is "'FML|HS\x00FML\x00FML|MP\x00FML\x00FORGE'"
MC-PROTO: 19450 read packet play.custom_payload
MC-PROTO: 19450 { channel: 'FML|HS', data: <Buffer 00 02 00 00 00 00> }

No longer kicked, but need to handle the plugin channels and FML handshakes

@deathcap
Copy link
Member Author

Getting further, to the mod list exchange:

client_forge $ NODE_DEBUG=mc-proto node client_forge.js localhost 25565
MC-PROTO: 24794 writing packet handshaking.set_protocol
MC-PROTO: 24794 { protocolVersion: 47,
  serverHost: 'localhost\u0000FML\u0000',
  serverPort: 25565,
  nextState: 2 }
MC-PROTO: 24794 writing packet login.login_start
MC-PROTO: 24794 { username: 'echo' }
connected
MC-PROTO: 24794 read packet login.compress
MC-PROTO: 24794 { threshold: 256 }
MC-PROTO: 24794 read packet login.success
MC-PROTO: 24794 { uuid: 'c39fa4c6-85a3-3582-8167-db3805dbe851',
  username: 'echo' }
MC-PROTO: 24794 read packet play.custom_payload
MC-PROTO: 24794 { channel: 'REGISTER',
  data: <Buffer 46 4d 4c 7c 48 53 00 46 4d 4c 00 46 4d 4c 7c 4d 50 00 46 4f 52 47 45 00 46 4d 4c> }
Server-side registered channels: [ 'FML|HS', 'FML', 'FML|MP', 'FORGE', 'FML' ]
MC-PROTO: 24794 read packet play.custom_payload
MC-PROTO: 24794 { channel: 'FML|HS', data: <Buffer 00 02 00 00 00 00> }
FML|HS { data: 
   { discriminator: 0,
     fmlProtocolVersionServer: 2,
     overrideDimension: 0,
     fmlProtocolVersionClient: undefined,
     mods: undefined },
  metadata: { size: 6 },
  buffer: <Buffer 00 02 00 00 00 00> }
MC-PROTO: 24794 writing packet play.custom_payload
MC-PROTO: 24794 { channel: 'REGISTER',
  data: <Buffer 46 4d 4c 7c 48 53 00 46 4d 4c 00 46 4d 4c 7c 4d 50 00 46 4d 4c 00 46 4f 52 47 45> }
MC-PROTO: 24794 writing packet play.custom_payload
MC-PROTO: 24794 { channel: 'FML|HS', data: <Buffer 01 02> }
MC-PROTO: 24794 writing packet play.custom_payload
MC-PROTO: 24794 { channel: 'FML|HS', data: <Buffer 02 00> }
MC-PROTO: 24794 read packet play.kick_disconnect
MC-PROTO: 24794 { reason: '"Mod rejections [FMLMod:IronChest{6.0.121.768}]"' }

The mod list will likely need to come from the ServerListPing, since a client must match the server and does not really need to implement all of the mods (for most bots/scripts/clients expected as the use case of NMP). But if I send:

         mods: [
           {name:'IronChest', version:'6.0.121.768'}
         ]

get an error encoding the mods array:

/Users/admin/games/voxeljs/node-minecraft-protocol/node_modules/protodef/dist/protodef.js:157
        throw e;
        ^

Error: SizeOf error for mods.2.0.name : missing data type: string
    at ProtoDef.sizeOf (/Users/admin/games/voxeljs/node-minecraft-protocol/node_modules/protodef/dist/protodef.js:140:15)
    at /Users/admin/games/voxeljs/node-minecraft-protocol/node_modules/protodef/dist/datatypes/structures.js:153:21
    at tryCatch (/Users/admin/games/voxeljs/node-minecraft-protocol/node_modules/protodef/dist/utils.js:33:12)
    at tryDoc (/Users/admin/games/voxeljs/node-minecraft-protocol/node_modules/protodef/dist/utils.js:40:10)
    at /Users/admin/games/voxeljs/node-minecraft-protocol/node_modules/protodef/dist/datatypes/structures.js:152:19
    at Array.reduce (native)
    at ProtoDef.sizeOfContainer (/Users/admin/games/voxeljs/node-minecraft-protocol/node_modules/protodef/dist/datatypes/structures.js:148:23)
    at ProtoDef.sizeOf (/Users/admin/games/voxeljs/node-minecraft-protocol/node_modules/protodef/dist/protodef.js:143:33)
    at /Users/admin/games/voxeljs/node-minecraft-protocol/node_modules/protodef/dist/datatypes/structures.js:96:28
    at tryCatch (/Users/admin/games/voxeljs/node-minecraft-protocol/node_modules/protodef/dist/utils.js:33:12)

@deathcap
Copy link
Member Author

Server ModList: [ { name: 'Forge', version: '11.15.0.1715' },
  { name: 'IronChest', version: '6.0.121.768' },
  { name: 'mcp', version: '9.18' },
  { name: 'FML', version: '8.0.99.99' } ]

"type": "byte"
},

// ServerHello
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(as I was saying in gitter), it might make more sense to use a big switch+ a mapper, like in https://github.com/PrismarineJS/prismarine-nbt/blob/master/nbt.json#L48 (or https://github.com/PrismarineJS/node-minecraft-protocol/blob/master/src/transforms/serializer.js#L21 )

  • mapper makes it possible for user to use the names (for example ServerHello instead of 0)
  • the big switch probably makes more sense because fields are distinct between each kind of "packet" here

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed, that's what I wanted to do but couldn't figure out how, to do: investigate those references

@deathcap
Copy link
Member Author

Server debug logging for reference: https://gist.github.com/deathcap/2c31e498b2375053c0e0

The handshake completes, but exits immediately after the FML handshake completes update: fixed, don't run your Forge server with -Dfml.debugNetworkHandshake=true; maybe need to hook up the "handshake complete state" to node-minecraft-protocol's notion of handshake/login complete, and also general refactoring

@deathcap
Copy link
Member Author

Moved tagHost \0FML\0 into the plugin. It is now enabled when forgeMods is set, in src/createClient.js, because it has to be set before setProtocol (since it is the plugin that sends the serverHost). With these changes, I believe this PR is ready.

Note: the tagHost option added to src/client/setProtocol.js could also be used for #336 BungeeCord protocol support

Note 2: the client_forge example demonstrates auto-negotiation, since that's the likely usage for Forge clients written with node-minecraft-protocol, but it could be refactored after #331 dynamic protocol support. Other enhancements/cleanup are possible, but I think this PR is good enough to pull at this point (pending any review feedback).

deathcap added a commit to deathcap/node-minecraft-protocols that referenced this pull request Feb 1, 2016
@rom1504
Copy link
Member

rom1504 commented Feb 1, 2016

Looks good to me, but I think it belongs in its own repo.

09:39 < rom1504> deathcap: https://github.com/PrismarineJS/node-minecraft-protocol/pull/326/files could be organized as https://github.com/andrewrk/mineflayer-navigate/ : forgeHandshake.js could be the index.js and client_forge.js could be the example.js
09:39 < rom1504> ah and if (options.forgeMods) forgeHandshake(client, options); should be in the client_forge.js / example.js imho

@rom1504 rom1504 mentioned this pull request Feb 2, 2016
@deathcap
Copy link
Member Author

deathcap commented Feb 3, 2016

Updated to fix conflicts with master but still needs to be moved into its own repo, re:gitter, agreed could be best served in a repository under the PrismarineJS organization in order to ease maintenance. src/client/forgeHandshake.js is easy to separate but will need to untangle the tagHost and autoVersion.

@rom1504
Copy link
Member

rom1504 commented Feb 3, 2016

@deathcap you should now be able to create a repo in PrismarineJS for that forge plugin . (Or move https://github.com/deathcap/node-minecraft-protocol-forge but then I think you should first remove almost all the commits, it's a plugin, not a nmp fork)

@deathcap
Copy link
Member Author

deathcap commented Feb 3, 2016

Cool thanks. Yeah https://github.com/deathcap/node-minecraft-protocol-forge branched from this repo (based on this PR) but I should probably rebase to remove the pre-Forge commits which were since removed as part of moving to a new module, or save the old branch and make a new one just with src/client/forgeHandshake.js

@deathcap
Copy link
Member Author

deathcap commented Feb 3, 2016

src/client/autoVersion.js has a dependency on src/client/forgeHandshake.js, though. I suppose it could be modified to support "hooks" into the response modinfo, which forgeHandshake could register itself as a handler somehow (for response.modinfo.type === 'FML', install itself with forgeMods: response.info.modList).

@rom1504
Copy link
Member

rom1504 commented Feb 3, 2016

Yes it would be better to remove that dependency somehow

@deathcap
Copy link
Member Author

deathcap commented Feb 7, 2016

Well it's easy enough to remove the autoVersion/forgeHandshake dependency, but it would remove the feature :). The trickiness is in having the plugin add Forge support to autoVersion, without necessarily enabling it in the handshake. Maybe the node-Minecraft-protocol-forge module could expose two plugins (forgeHandshake and autoVersionForge?)

deathcap added a commit to PrismarineJS/node-minecraft-protocol-forge that referenced this pull request Feb 7, 2016
@deathcap deathcap changed the title Forge client support Forge client support (support integrating with node-minecraft-protocol-forge) Feb 7, 2016
@deathcap
Copy link
Member Author

deathcap commented Feb 7, 2016

Alright I've rebased https://github.com/deathcap/node-minecraft-protocol-forge, and updated this PR so it now only contains the changes needed to support node-minecraft-protocol-forge rather than the code itself. To allow auto-versioning Forge plugins, added a new "auto version hook" feature, where plugins can add their own handlers for the auto-version ping response. autoVersionForge uses this to install itself with the right mods. Example usages of these two plugins:

var mc = require('minecraft-protocol');
var forgeHandshake = require('minecraft-protocol-forge').forgeHandshake;
var client = mc.createClient({
    host: host,
    port: port,
    username: username,
    password: password
});

forgeHandshake(client, {forgeMods: [
  { modid: 'mcp', version: '9.18' },
  { modid: 'FML', version: '8.0.99.99' },
  { modid: 'Forge', version: '11.15.0.1715' },
  { modid: 'IronChest', version: '6.0.121.768' }
]);

and:

var mc = require('minecraft-protocol');
var autoVersionForge = require('minecraft-protocol-forge').autoVersionForge;
var client = mc.createClient({
    version: false,
    host: host,
    port: port,
    username: username,
    password: password
});

autoVersionForge(client);

Not bad, imho. Open to other improvements but once these changes are in, will be able to publish node-minecraft-protocol-forge.

@rom1504

@deathcap you should now be able to create a repo in PrismarineJS for that forge plugin

OK I tried to create a new repo in PrismarineJS but am only offered:

screen shot 2016-02-07 at 12 01 26 am

can I just transfer https://github.com/deathcap/node-minecraft-protocol-forge to https://github.com/PrismarineJS/node-minecraft-protocol-forge? (there is a 'transfer' option in https://github.com/deathcap/node-minecraft-protocol-forge/settings but it is in the 'danger zone' so wanted to be sure)

@rom1504
Copy link
Member

rom1504 commented Feb 7, 2016

Yeah just use transfer.

@rom1504
Copy link
Member

rom1504 commented Feb 7, 2016

Looks good to me. Is this ready to merge ?

@rom1504
Copy link
Member

rom1504 commented Feb 7, 2016

Oh and you probably got an invitation to join prismarinejs in your mail, looks like you didn't accept it yet.

@deathcap
Copy link
Member Author

deathcap commented Feb 7, 2016

Yep ready to merge!

I'll check for the invite, won't let me transfer to PrismarineJS without it (says no rights)

update: transfer complete: https://github.com/PrismarineJS/node-minecraft-protocol-forge

rom1504 added a commit that referenced this pull request Feb 7, 2016
Forge client support (support integrating with node-minecraft-protocol-forge)
@rom1504 rom1504 merged commit 119d485 into PrismarineJS:master Feb 7, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants