mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-14 15:24:52 +09:00
Reject an upload for a player who is back, and take an empty reply as stored
A player rejoining before the controller's reply was skipped by reject_upload, and the join then kept the copy the controller refused. The rejection now counts while the join still waits for its acquire reply, which the controller sends after it. A controller from before the upload response replies with nothing. That failed validation on a newer host, so its uploads were never confirmed. It is taken as stored, as before. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JyNnVzRW4jPWaoWTxCyhM7
This commit is contained in:
1 parent
b6bc118297
commit
a959ff25a4
3 files changed
+18
-4
No files matched your search
@@ -269,12 +269,13 @@ export class UploadResponse {
|
||||
) {
|
||||
}
|
||||
|
||||
static jsonSchema = Type.Object({
|
||||
"stored": Type.Boolean(),
|
||||
// No type: a controller from before this response replies with nothing, which counts as stored
|
||||
static jsonSchema = Type.Unsafe<{ stored: boolean } | undefined>({
|
||||
properties: { "stored": { type: "boolean" } },
|
||||
});
|
||||
|
||||
static fromJSON(json: Static<typeof UploadResponse.jsonSchema>) {
|
||||
return new this(json.stored);
|
||||
return new this(json?.stored ?? true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -735,10 +735,15 @@ end
|
||||
-- too, and then downloads what this save sent.
|
||||
function player_sync.reject_upload(player_name, generation)
|
||||
local player = game.get_player(player_name)
|
||||
if not player or player.connected then
|
||||
if not player then
|
||||
return
|
||||
end
|
||||
local script_data = get_script_data()
|
||||
-- A player who is back already still waits for the acquire reply, which the controller sent after this one, and
|
||||
-- that join then downloads. Past that reply the join has decided and this is not about the copy here any more.
|
||||
if player.connected and not script_data.players_waiting_for_acquire[player_name] then
|
||||
return
|
||||
end
|
||||
local player_record = script_data.players[player.name]
|
||||
if not player_record or player_record.generation ~= generation then
|
||||
return
|
||||
|
||||
@@ -60,6 +60,14 @@ describe("player_sync plugin", function() {
|
||||
assert(!validate({ instanceId: 1, playerName: "player", playerData: data }), `accepted ${deleted}`);
|
||||
}
|
||||
});
|
||||
it("should take the empty reply of an older controller as a stored upload", function() {
|
||||
const validate = lib.compile(msg.UploadResponse.jsonSchema);
|
||||
assert(validate(undefined));
|
||||
assert.equal(msg.UploadResponse.fromJSON(undefined).stored, true);
|
||||
assert(validate({ stored: false }));
|
||||
assert.equal(msg.UploadResponse.fromJSON({ stored: false }).stored, false);
|
||||
assert(!validate({ stored: "no" }));
|
||||
});
|
||||
describe("SetPlayerDataRequest schema", function() {
|
||||
const validate = lib.compile(msg.SetPlayerDataRequest.jsonSchema);
|
||||
// Shaped like module/serialize.lua output
|
||||
|
||||
Reference in new issue
Block a user