Gruß-Anfang und Ablehnung festschreiben
Ein Test hält fest, dass ein Gruß mit Kennung und Version beginnt und eine Ablehnung aus Kennung und Grund besteht. Beides bleibt auch bei einer neuen Protokollversion gleich, damit sich Spiele verschiedener Versionen weiter sagen können, dass sie nicht zusammenpassen. Die Meldung des festgeschriebenen Nachrichtenformats weist darauf hin. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -19,16 +19,20 @@ use crate::world::TICKS_PER_SECOND;
|
|||||||
/// Version of the messages. Raise it whenever a message changes.
|
/// Version of the messages. Raise it whenever a message changes.
|
||||||
pub const PROTOCOL_VERSION: u32 = 1;
|
pub const PROTOCOL_VERSION: u32 = 1;
|
||||||
/// Longest message a guest may send, in bytes. The longest one is a greeting
|
/// Longest message a guest may send, in bytes. The longest one is a greeting
|
||||||
/// with a name of full length, which needs 65 bytes.
|
/// with a name of full length, which needs 65 bytes. Greetings of every
|
||||||
|
/// future version must fit as well: a longer one is rejected unread instead
|
||||||
|
/// of being recognised as another version.
|
||||||
pub const MAX_GUEST_MESSAGE_BYTES: usize = 128;
|
pub const MAX_GUEST_MESSAGE_BYTES: usize = 128;
|
||||||
/// Ticks between two state checksums in the stream of ticks: one second.
|
/// Ticks between two state checksums in the stream of ticks: one second.
|
||||||
pub const CHECK_INTERVAL_TICKS: u64 = TICKS_PER_SECOND as u64;
|
pub const CHECK_INTERVAL_TICKS: u64 = TICKS_PER_SECOND as u64;
|
||||||
|
|
||||||
|
// Never changes, see the module documentation.
|
||||||
const TO_HOST_HELLO: u8 = 1;
|
const TO_HOST_HELLO: u8 = 1;
|
||||||
const TO_HOST_ACT: u8 = 2;
|
const TO_HOST_ACT: u8 = 2;
|
||||||
const TO_HOST_DIVERGED: u8 = 3;
|
const TO_HOST_DIVERGED: u8 = 3;
|
||||||
|
|
||||||
const TO_GUEST_WORLD: u8 = 1;
|
const TO_GUEST_WORLD: u8 = 1;
|
||||||
|
// Never changes, see the module documentation.
|
||||||
const TO_GUEST_REFUSED: u8 = 2;
|
const TO_GUEST_REFUSED: u8 = 2;
|
||||||
const TO_GUEST_TICK: u8 = 3;
|
const TO_GUEST_TICK: u8 = 3;
|
||||||
|
|
||||||
@@ -75,7 +79,8 @@ impl Action {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Why a host turned a guest away.
|
/// Why a host turned a guest away. The codes never change, because games of
|
||||||
|
/// other versions read them.
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum Refusal {
|
pub enum Refusal {
|
||||||
/// The guest's game speaks another version of these messages.
|
/// The guest's game speaks another version of these messages.
|
||||||
|
|||||||
+41
-2
@@ -12,7 +12,7 @@ use grovewright_core::command::Command;
|
|||||||
use grovewright_core::hash::fnv1a64;
|
use grovewright_core::hash::fnv1a64;
|
||||||
use grovewright_core::map::{Map, Pos, Terrain, Tile};
|
use grovewright_core::map::{Map, Pos, Terrain, Tile};
|
||||||
use grovewright_core::player::PlayerId;
|
use grovewright_core::player::PlayerId;
|
||||||
use grovewright_core::protocol::{Action, Refusal, ToGuest, ToHost};
|
use grovewright_core::protocol::{Action, Refusal, ToGuest, ToHost, PROTOCOL_VERSION};
|
||||||
use grovewright_core::replay::Recording;
|
use grovewright_core::replay::Recording;
|
||||||
use grovewright_core::rules::{
|
use grovewright_core::rules::{
|
||||||
end_hash, frozen_offline_script, frozen_script, rules_id, RULES_SEED,
|
end_hash, frozen_offline_script, frozen_script, rules_id, RULES_SEED,
|
||||||
@@ -162,6 +162,45 @@ fn the_protocol_format_is_unchanged() {
|
|||||||
fnv1a64(&bytes),
|
fnv1a64(&bytes),
|
||||||
0xc263_69c0_f892_247b,
|
0xc263_69c0_f892_247b,
|
||||||
"the messages between host and guests changed; raise \
|
"the messages between host and guests changed; raise \
|
||||||
PROTOCOL_VERSION in core/src/protocol.rs; then update this value"
|
PROTOCOL_VERSION in core/src/protocol.rs; then update this value. \
|
||||||
|
The start of a greeting and the refusal must never change, see \
|
||||||
|
the_greeting_start_and_the_refusal_never_change"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Covers the promise that games of every version can still tell each other
|
||||||
|
/// that they do not fit: a greeting starts with its tag and the version, and
|
||||||
|
/// a refusal is its tag and the code of its reason. Unlike every other part
|
||||||
|
/// of the messages, these stay the same when PROTOCOL_VERSION is raised.
|
||||||
|
#[test]
|
||||||
|
fn the_greeting_start_and_the_refusal_never_change() {
|
||||||
|
let hello = ToHost::Hello {
|
||||||
|
rules: 1,
|
||||||
|
key: [3; 16],
|
||||||
|
name: "Ada".to_owned(),
|
||||||
|
}
|
||||||
|
.encode();
|
||||||
|
let mut start = vec![1];
|
||||||
|
start.extend(PROTOCOL_VERSION.to_le_bytes());
|
||||||
|
assert_eq!(
|
||||||
|
hello[..start.len()],
|
||||||
|
start[..],
|
||||||
|
"a greeting starts with the tag 1 and the version; this never \
|
||||||
|
changes, not even with a new PROTOCOL_VERSION"
|
||||||
|
);
|
||||||
|
let refusals = [
|
||||||
|
(Refusal::OtherProtocol, 1),
|
||||||
|
(Refusal::OtherRules, 2),
|
||||||
|
(Refusal::WorldFull, 3),
|
||||||
|
(Refusal::TooManyPlayers, 4),
|
||||||
|
(Refusal::AlreadyPlaying, 5),
|
||||||
|
];
|
||||||
|
for (refusal, code) in refusals {
|
||||||
|
assert_eq!(
|
||||||
|
ToGuest::Refused(refusal).encode(),
|
||||||
|
vec![2, code],
|
||||||
|
"a refusal is the tag 2 and the code of its reason; this never \
|
||||||
|
changes, not even with a new PROTOCOL_VERSION"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user