I would make a PR for it but I think it will require more work than it looks to keep it compatible with previous forge versions (if possible at all) so i want to discuss it first.
As an example situation, let's say you have a mod where previously you didn't think about setting any acceptableRemoteVersions string, and you updated your mod in a way that is completely network compatible, so you want old client to be able to connect to new server, and new client to old server.
Currently this is impossible. The older side will always reject the connection.
Currently, it's possible to workaround it using custom NetworkCheckHandler, but it requires both versions to already have that special NetworkCheckHandler implemented. It involves always accepting the connection to/from newer version, and letting that newer version actually decide (this also requires the older version to actually know that it's the older one).
A simple implementation of this proposal would actually break the previously mentioned NetworkCheckHandler, so I think the best approach would be to deprecate NetworkCheckHandler and add new annotation (name?) or show a warning at runtime if a method with currently expected sugnature is annotated, and allow to use new signature (return type).
My proposal would be 2 changes:
NetworkCheckHandler return an enum with the following values possible:
FORCE_REJECT
UNDECIDED
ALLOW
FORCE_REJECT would be the equivalent of currently returning false. UNDECIDED would forward the decision to the other side, and if both are undefined - connection would be rejected.acceptableRemoteVersions is interpreted: if the remote version doesn't match, it should behave as if the NetworkCheckHandler returned UNDECIDED for that version.These changes would allow an easy way to allow a new version to allow connecting to old version that isn't aware of the new version. This also shouldn't be any security issue because malicous client can already fake the version string anyway.
Generally mods have matching versions on client and server, so I don't think making this situation easier is a very high priority.
What situation are you seeing where clients and servers commonly have different versions?
It seems like a fine idea but I don't really understand the real-world context here.
I ran into situation where i created optifine compatibility update that just doesn't need any server update at all. Same could apply to any other client only fixes it anything that only fixes dedicated servers (that would be rare). But it would still be impossible to connect with new client no matter what I do (unless the previous version already had the right network check handler). While right now it's not high priority because the mod is still in early testing anyway, I wouldn't want to force server owners to update when there is no need for it. While with mod packs it's true that servers will just update the whole modpack, not everyone will run actual big modpack. Having the option to avoid unnecessary server downtime just because i release a client only fix would be nice.
This is why you should use a logical versioning scheme and then enforce it with your remote version matching.
While it may apply when you have already version like,1.6.2.0 it doesn't apply when the mod is still in 0.0.X.0 version. And this proposal would allow for much less cumbersome way to connect to servers from dev environment (without faking the version number).
And in case of my mod the vast majority of updates will be network compatible even if the api won't be compatible. So logical versioning scheme won't allow it. Currently the old version has to be already aware that a new version may be compatible.. And again while its possible with special network check handler i think the code for it is much more complex than necessary and not that easy to actually test.
Unfortunately, it is dangerous to do that because then it takes away your ability to force update both sides when needed. If you tell the server "accept all newer clients", then how do deal with the case when both sides need to be updated?
If you really want a mechanism to allow this, I think you should create an additional custom mechanism to version changes in between those that need to be matched. Then you would actually re-publish the mod so it reports as the same as your previous version but then you have a custom packet that gives extended version info if you want it. Basically you would have a scheme that is major.minor.build.revision but you would publish the major.minor.build and communicate revision separately. I think that solves your problem without losing the benefits of version locking. Lock to build, but allow revisions.
I might be reading this wrong, but lying and republishing a different binary that claims to be an older version sounds like a terrible idea, imho.
Because once you release a binary, you can never be certain that no one is not running it somewhere.
So now you have two binaries that strangely report themselves equivalent, little way to disambiguate them, and even then your strategy isn't guaranteed to reach all of your users. Someone could just go on Curse (for example), see that "version 1.1" is listed, think "well, I have the 1.1 I downloaded a couple weeks ago I should be good", oblivious to the fact that it's actually "1.1 but not really there's extra stuff in there"
I think a (by no means the only) proper way to resolve this is to completely decouple "can connect with each other" from the mod version. This is how vanilla does it with protocol version numbers.
Introduce a separate piece of metadata, protocol version, to @Mod. When connecting to a server, exchange this number and refuse to connect if not equal. Then, it's up to the modder themselves to bump the protocol version whenever incompatible changes occur.
Upsides: Extremely simple, allow-by-default
Downsides: Allow-by-default, relies on modders to know when to bump the numbers and actually do so, hard to enforce retroactively (wait until 1.13)
It does allow the ability to force update both sides using the FORCE_REJECT. It's enough that one side does it. And it would also be worth considering ability to change the behavior of acceptableRemoteVersions by additional flag which would allow modders to decide on how to interpret it. It would be enough for one side to have the force reject interpretation to actually reject the connection.
And 2 a with the same version is something i find problematic. Forge will show only the version forge knows about. The real version wouldn't be included in crash reports for example.
Also your solution is basically a hack owe version of what led suggested and the same issue applies.
Right now i have code that accomplishes what i wanted but i really don't like the way it's done. One side detects itself as the older one and always accepts the connection and the other side actually checks the version and either accepts or rejects the connection (and when ant parse version it accepts it as it's most likely dev environment or build from other branch).
Adding that feature to forge would allow me to just specify the allowed remote versions without extra code and it would guarantee that it works correctly.
When I say re-publish with the same version number, I don't mean hide it from the user I mean it as follows.
The user would download versions, like 2.6.3.1, then 2.6.3.2 which would be clearly marked in the download links, jar name, etc. However, the Forge versioning would sync based on the reported 2.6.3 (i.e. not the final revision number). So all 2.6.3.x would be accepted either way. If for some reason you cared (but you're not supposed to care based on your reason for wanting this) you can communicate the .x part across to other side.
I'm just saying that overall you cannot know in advance what you will want to reject or accept. Imagine someone "accidentally" makes a server side mod that accepts all future versions. You'd never be able to recover from that.
So I think you have to make this mechanism intrinsic to the level within the versioning, meaning that the major.minor.build would need to be locked but you could do a .revision which would always be accepted. Then you have full freedom to get both mechanisms working for you.
To put it another way, if the other side is really not supposed to care then you don't need to report it. The only thing you want is for the user to know whether they have a version with the modified fix/feature which you can do descriptively without any new Forge mechanism.
what you're describing is basically my protocol number proposal, except the number is cleanly separated out from the version string, instead of imposing some rule like "the last .x doesn't count" ;P
Actually looking at it protocol version number would be a way better approach, rather than this hacky solution.
So it'd be something like adding
String networkVersion() default version();
to @Mod if that syntax was possible? Sounds simple and backward-compatible to me.
that would work. I can make PR for that.
Big thumbs up for the protocol version number approach. :+1:
If the allow-by-default behavior isn't something you want, you could possibly fall back to plain old version comparison if a protocol version was never specified?
Either that or the protocol version would be the mod version number. I think special casing no protocol version would be better. This would make it possible to distinguish for example "protocol version 1.2.3" and "mod version 1.2.3 without protocol version specified"
As for PR - wanted to do it the previous weekend but got busy with other stuff.
Does it really make sense to use a "proper" versioning scheme for a protocol version?
In my opinion a simple integral value is suffient for the reasons stated above. Additionally this would allow Forge to do the necessary checks.
I'm basically wondering what your usecases for a more complex protocol version number are.
I agree with @TheMrMilchmann and @quat1024. I think that valid protocol versions should be ≥ 1, then the default value could be 0 (or ≥ 0 and default -1). That way forge could easily fall back to the current method (to avoid breaking binary compat.)
Out of interest, wouldn't a semvar approach be better than adding an extra field to @mod? That way you can say, for instance, that patches are allowed to be loaded without any rejection but specify that major releases (like breaking API releases) should be updated to match the new version.
That's how npm, the package manager for node applications (Javascript) does it. A package.json file (configuration file for the project looks always like this):
"dependencies": {
"pouchdb": "^6.3.4",
"react": "^15.6.1",
"redux-features": "^3.0.0",
"redux-thunk": "^2.2.0",
"reselect": "^3.0.1",
"source-map-support": "^0.4.15"
},
"devEngines": {
"node": ">=7.x",
"npm": ">=4.x",
"yarn": ">=0.21.3"
}
}
If you then want to update your dependencies, you simply type yarn upgrade and it will upgrade your dependencies so they will still be backwards compatible. This ensurs that your program does not break due to API changes and will make sure that you have the newest version installed for bug fixes.
that requires modders to follow semver (when some don't want to and also want to use this system). I think it also conflates code compat (which is what semver is meant for), with network compat (which is what this discussion is about).
The code can be binary-incompatible due to public API changes, etc, but can still communicate with older versions of itself just fine. This idea would allow for that.
That has already been discussed @SirWindfield. Most mods are updated very incrementally and not batched into big major version changes. A mod can have many breaking changes over a relatively small amount of time, which are mostly unrelated to network changes.
This issue has been automatically marked as stale because it has not had activity in a long time. If this issue is still relevant and should remain open, please reply with a short explanation (e.g. "I have checked the code and this issue is still relevant because ___." or "Here's a screenshot of this issue on the latest version"). Thank you for your contributions!
I'm still willing to make a PR for that, the only reason I didn't was that I didn't know what way it should be done, and the discussion didn't point to any good solution. Or is some form of it going to be in 1.13?
This issue has been automatically marked as stale because it has not had activity in a long time. If this issue is still relevant and should remain open, please reply with a short explanation (e.g. "I have checked the code and this issue is still relevant because ___." or "Here's a screenshot of this issue on the latest version"). Thank you for your contributions!
In my opinion this should not be stale, as I'd agree that a seperate protocol version would be quite useful.
What about 1.13?
1.13 would be your chance to break compatibility with old Forge/Mod versions, so you'd have better luck there, yes.
I have no intention of screwing with this in 1.13 myself as the current format works just fine I still dont see a good use/implementation of this.
Honestly I think it's reversing the issue, How would something that already exists know if a future version breaks network compatibility?
The other way around, a new version knowing which old versions to reject is actually possible.
So both sides have a 'minimum version to allow' as their network check. And if one of them doesn't find the minimum version then reject and fail.
A: 1.1 (>=1.0)[1] B: 1.2 (>=1.0)[1] C: 1.3 [incompatible protocol change] (>=1.3)[2] D: 1.4 [compatible protocol extension] (>=1.3)[2,3]
| | A | B | C | D |
|----|----|----|----|----|
|A|true|true|true|true|
|B|true|true|true|true|
|C|false|false|true|true|
|D|false|false|true|true|
Seems like a logical setup to me that should work for everything you need.
Leaving it up the the future versions to reject known old ones, instead of old ones guessing at which future ones to accept.
The only thing that might be tricky is if you have an 'optional enhancement' to the network protocol. Because you don't want to strictly break compatibility between versions, but want to enable new features if both sides know about it.
The best way to do that would be to negotiate a separate 'supported' protocol version. But that's up to you, after the initial connection is made. I would highly suggest one of your first 'hello' packets is a C->S 'I support these protocol versions' and a S->C: 'Cool lets use #x'
Here is a chart showing the selected protocol after that hello packet. Blanks are rejected combos from the above chart.
| | A | B | C | D |
|---|---|---|---|---|
| A | 1 | 1 | | |
| B | 1 | 1 | | |
| C | | | 2 | 2 |
| D | | | 2 | 3 |
The other option is we scrap the entire system we have now and force modders to specify a separate 'protocol version' and do that initial hello for them, with some form of 'player.getProtocolVersion("modid")' but that would need to be written, and it would honestly probably ot be understood by 90% of modders so we'd have to come up with a sane default.. Which would do what most people expect ... which is difficult.
This is very close to my initial proposal that wasn't a protocol version. My idea for negotiation was that "the newer version knows better" so the newer version should decide what to do. With my original proposal with NetworkCheckHandler, the older version could just check if it's older, and return UNDECIDED, and the newer one would actually decide what to do.
The bulk of my comment is what already exists today if you use it correctly.
The "newer versions knows better" works well in a "accept anything higher then I am"
You're trying to reverse that into "accept only this small range ending with my version" which I don't think is logical or needed.
Basically you want to change allowed = server && client to allowed == server || client Which honestly I think adds way to much complexity to the system. And breaks the simple setup described in my post.
I think the crux of your problem is your example let's say you have a mod where previously you didn't think about setting any acceptableRemoteVersions string.
In those cases, it's just gunna have to be a "sucks to be you, learn how to use features of Forge in the future"
Forge has to set reasonable defaults for values modders don't supply. And the vast majority of modders DO NOT make network compatible updates so the default is to be strict.
Most helpful comment
that requires modders to follow semver (when some don't want to and also want to use this system). I think it also conflates code compat (which is what semver is meant for), with network compat (which is what this discussion is about).
The code can be binary-incompatible due to public API changes, etc, but can still communicate with older versions of itself just fine. This idea would allow for that.