Closed iasoon closed 6 years ago
Just to throw some ideas around:
After discussing face-to-face, we're pretty convinced we want the first option. This would be implemented on both ends of the wire, as utils to the game implementation and client.
When we want to hand the bot control over syncing, this can be arranged between bot and client.
This is something we want in the long term, but let's ask the acute question: Do we want a turn number in the planetwars protocol to prevent desynchronisation? If so, I'd just do that in an ad-hoc fashion and have bots put the number in their response (perhaps optionally). If not, we keep the fancyness for a later stage of MOZAIC.
We had some discussion on this today, concluding that in general turn-based games don't have a request/response flow, but rather a request/response/feedback flow. For planetwars, that is gamestate / commands / validation of commands. It seems kind of challenging to me to build an abstraction on this, though, eh, I think? I guess that all three steps should be logged client-side. Eh, that kind of makes it easy. idk I'm tired
The 'hard' part is that this sequence kind of bridges different 'responsibilities': there is interaction between things we previously identified as totally separated 'layers' (game data and metadata such as logging). These three messages would have to be synchronised together, which makes them very much tied to eachother. @wschella, any thoughts?
Quick question while i think further, can we make the validation piggyback on the next gamestate? (That's what we're doing now right?)
No, we don't send any validation right now. @ajuvercr suggested piggybacking as well, but I don't really see the advantage to this? The format would become more difficult (eg the first state has no piggybacked validation), you don't save any bandwidth or round-trips, and I'd imagine the handling code would be more difficult as well, on both ends.
Speaking of the handling code, I think the main issue with this scheme would lie there: we'd need a framework-level thing that makes this three-way interaction work, with the proper sequention number (= the turn number) set.
For clarity, what are we considering as validation, and why do bots need to receive it synced with the other messages?
Validation is 'feedback' on the input your bot provided. Parse errors, logic errors etc. are reported here. For planetwars, that would be a parse error, a dispatch error, or a confirmation of the dispatch. This information is required because it is both the debug information for your bot and needed to construct the game log.
It has to be "synced" with the other messages so that you can determine which command the feedback message describes, just like we need synchronisation to know what game state a command was the reply to. Note that "synchronising" here just means tagging them with a common token (e.g. the turn number). The reason why this might be though to implement cleanly is that we can't make this token internal to the framework, since the game code will have to parse a reply and send feedback afterwards. For sending the feedback, the user code would need this token. While this is not really an issue, it seems error-prone and a bit dirty. Compare this to a two-way request/reply sequence, where the synchronisation token could be completely internal to the code handling the interaction.
I think validation is not relevant for the bot (only the player), which is a small but important distinction! I'mma sleep over it.
I think we certainly want the request/response thing because it is a quite common pattern, it would be ideal if we could do the third message in a higher level protocol. I was just thinking about the possibly different semantics of a request over a network compared to an acked message. Consider the following situation: a client receives a request, and is then killed for a random reason. When the client reconnects, the request would not be resent because it is already acked, while desired behaviour would be to resend the request because it was not answered yet. This could be relevant for games with long turns (cfr online chess/risk/go). Do we want to account for this and make requests a primitive in our networking protocol?
Allright, mini-RFC!
Firstly, let's summarize the problem: A straightforward requirement for implementing games is waiting for player input. Often we want to constrain how long we wait for this input. This is all not that difficult, but problems start to arise when players don't respect their time limit, or when the network has a sudden delay, resulting in a message that arrives after its time-out expires. We want to avoid that such a stray message is interpreted as the next message that is expected (e.g. the commands for the next turn). This problem is resolved by annotating each command with an identification token, such as a turn number, to verify a messages 'identity'. Of course, this is an ad-hoc solution and does not generalize well.
So, as I see it, the actual problem is identifying arriving messages as specific messages as something that we were waiting for. I think the most elegant way to solve this would be a "message/response"-protocol, in which messages are exchanged freely between client and server, annotated with an unique message number (note that this number is different from a sequence number, as the latter does not have to be deterministic). One can then send a response to one of these 'message'-messages by sending a 'response'-message with the same message number. When a response is received, the implementation checks the validity of the response (did we request this response, did it arrive timely, did the right player answer), and if all is well, it is passed on to the game implementation. This way, each message implicitly serves as a 'synchronisation point', by offering a 'synchronisation token' in the form of the message number. Whether this synchronisation point is actually used is entirely up to the library user. This means that responding to a message is entirely optional.
As for a programming interface, there would be an interface for sending messages to players, yielding the message identification token. This identification token can then be used for, well, identifying responses, which can then in turn be used to implement e.g. a turn lock in which each player should send a command each turn.
Expanding on that simple example of a turn-based game:
The main advantage of this approach is that there is no distinction between messages that need an answer, and messages that don't. This way, one can implement a protocol as an enumeration of all valid messages (be it requests like in the turn-based example, random info messages, log entries, ...). When receiving a message, the implementator can switch on the decoded result and handle the message appropriately. When receiving a response, the implementator already knows the expected format of the response (because he knows what he requested), and can decode the message without having to switch (and have to reject all other options).
Looks good. I can't think of disadvantages with this approach. This also allows you to create a blocking function that sends a message, waits until a response is received or the timeout passes, and returns the response content.
This RFC has been implemented on the networking/message-response
branch. It needs some work, but it is functional.
As @ajuvercr pointed out, there is a slight issue with client-server synchronisation when using the current communication scheme. The issue occurs when the command for a previous turn is received when the game server has already stepped, causing an unintentional command to be executed.
I think the best abstraction for this issue would be an abstraction over the request/response pattern:
request(client, message, timeout)
Of course, one could wait for multiple requests to finish before continuing, for example for all players to choose their move. So, this scheme would be a generalization over the
StepLock
we have now.The main question is how this should fit in the current design. I think it is clear that this is not something that happens on a framework level, since that would be limiting in many ways. It should be an util, on the same level as the
StepLock
we use now. This means that we cannot leverage the MOZAIC protocol for this, and we have to somehow get this request number into the game protocol. I'm not sure how we can do this in a hassle-free way. Suggestions?As for the client-side of things: I imagine lines could be blurred a bit here, either handling requests in the client or bot, depending on the specific needs the game implementer has.