It is currently 16 Apr 2024, 22:01
   
Text Size

Best practices for distributed Magic play

General Discussion of the Intricacies

Moderator: CCGHQ Admins

Best practices for distributed Magic play

Postby silly freak » 03 May 2013, 22:50

Hi all!

After my latest attempt on a Magic engine ended after I realized that I had fundamental flaws at the lowest level, I was now thinking again about what is really necessary to support a Magic engine that will survive adding network play in different variants.

With different variants, I basically mean these features:
  • A classic client-server model, where the clients do not run the game engine themselves, but only receive the information they need (here, I don't yet demand that a malicious client couldn't cheat)
  • A true peer-to-peer solution, where the clients all run game engines, but they are kept in sync, ideally even across multiple versions of the software (to some extent of course)
  • A combination thereof, e.g. clients connected to different servers that have mirrored game engines
  • Spectators that view, but don't participate in the match (e.g. a GUI for an AI only match)

Besides that, there are a few other requirements:
  • For client-server, the clients shouldn't be able to cheat. That can be quite sophisticated. For example, a client should be able to tell that a library was shuffled, but must not be able to infer from any IDs what the shuffling did to the library.
  • For mirroring, it shouldn't be necessary to transmit game states, but rather only actions that modify the game state (or is there a third option?) Implications are that object references must be somehow valid across the network, and even worse, objects created through the action must receive the same ID on all mirrored engines.
    Ideally, the transmitted actions are still on the semantic "Magic" level, e.g. "attack with these creatures" instead of "tap these creatures and add them to combat." I don't know, but I feel that would be the better way to transmit actions.

With transmittable actions, there come a few other features that would be great and shouldn't add too much additional work at that point; undo and game persistence, namely. Undo adds the possibility for Min/Max and possibly other AI algorithms, besides being nice for the user, and game persistence is practically self implementing when even a remote engine can be initialized with a list of actions taken.


I thought I wouldn't be the only one interested in these things, and I haven't sorted out what is the right thing to implement first so that my work stands on a solid foundation. Do any of you have ideas on these topics, already implemented or otherwise? Do you see other questions that I forgot to ask?
I'm not looking for a how-to or something. I'd like to discuss here, not necessarily answer the questions directly. Getting a better understanding of what is and what isn't needed is half the work.
___

where's the "trust me, that will work!" switch for the compiler?
Laterna Magica - blog, forum, project, 2010/09/06 release!
silly freak
DEVELOPER
 
Posts: 598
Joined: 26 Mar 2009, 07:18
Location: Vienna, Austria
Has thanked: 93 times
Been thanked: 25 times

Re: Best practices for distributed Magic play

Postby Huggybaby » 08 May 2013, 14:30

Thanks for this Silly Freak. Network implementation is a special and neglected situation. We have some awesome AI opponents now but in paradise every game would be networked and that is far from the case.
User avatar
Huggybaby
Administrator
 
Posts: 3205
Joined: 15 Jan 2006, 19:44
Location: Finally out of Atlanta
Has thanked: 696 times
Been thanked: 594 times

Re: Best practices for distributed Magic play

Postby Arch » 09 May 2013, 17:22

There are a bunch of large topics here which makes it harder to comment on.

silly freak wrote:After my latest attempt on a Magic engine ended after I realized that I had fundamental flaws at the lowest level, I was now thinking again about what is really necessary to support a Magic engine that will survive adding network play in different variants.

With different variants, I basically mean these features:
  • A classic client-server model, where the clients do not run the game engine themselves, but only receive the information they need (here, I don't yet demand that a malicious client couldn't cheat)
  • A true peer-to-peer solution, where the clients all run game engines, but they are kept in sync, ideally even across multiple versions of the software (to some extent of course)
  • A combination thereof, e.g. clients connected to different servers that have mirrored game engines
  • Spectators that view, but don't participate in the match (e.g. a GUI for an AI only match)
I think that an engine that can be used with any network solution is most likely not defined in such terms. There shouldn't be any need to consider networking when building the engine. The features required, from an engine point of view, for gaming over a network are the same as when gaming locally.

Regarding network architecture I'd suggest client-server. I think this is the easiest to reason about and it also addresses the cheating problem pretty well. (Use a trusted third-part server.) Client-server can also mimic client-client (duelling) setups by simply having one client host the server transparently.

silly freak wrote:Besides that, there are a few other requirements:
  • For client-server, the clients shouldn't be able to cheat. That can be quite sophisticated. For example, a client should be able to tell that a library was shuffled, but must not be able to infer from any IDs what the shuffling did to the library.
  • For mirroring, it shouldn't be necessary to transmit game states, but rather only actions that modify the game state (or is there a third option?) Implications are that object references must be somehow valid across the network, and even worse, objects created through the action must receive the same ID on all mirrored engines.
    Ideally, the transmitted actions are still on the semantic "Magic" level, e.g. "attack with these creatures" instead of "tap these creatures and add them to combat." I don't know, but I feel that would be the better way to transmit actions.
Cheating is tricky. I'd aim for making it impossible for client to cheat by not giving them information that can be used to cheat. The server should decide what can be viewed by each player and only transmit that data to the client. Since hidden information can not affect the game the engine would be able to function fully without it.

Code: Select all
    End of upkeep step.
    P1 sends 'pass' to server.
    Server approves 'pass' and sends P1's 'pass' to P2 which P2 applies to it's state.
    P2 sends 'pass' to server.
    Server does the following:

       Applies the 'pass' to it's own state. Sees that P1 will draw a
       card that it has not been informed about.

       P1 is sent P2 'pass' and the top card of it's library.

       P2 does not require any more information other than that the
       'pass' action was ok.
This is working from the assumption that both the server and the clients are all using the same engine. P1 engine will move a card from library to hand, since it was revealed by the server the player can become aware of what card it is. P2 engine will move an anonymus card from library to hand (for P1), this can be any of the cards in the library since they should all be anonymus.

As long as all parties involved are using the same engine this answers part of your mirroring question. The same action can be applied at all locations and have the same outcome. The missing piece would be "randomly" generated things such as card/object-ids. This can be solved by using the same random generation algorithm and seed for all engine. Just distribute the seed at the start of the game and make sure you don't use any side effects in the engine.

As long you're using smart clients (that are aware of the logic) I agree that using a higher semantic level is better.

silly freak wrote:With transmittable actions, there come a few other features that would be great and shouldn't add too much additional work at that point; undo and game persistence, namely. Undo adds the possibility for Min/Max and possibly other AI algorithms, besides being nice for the user, and game persistence is practically self implementing when even a remote engine can be initialized with a list of actions taken.
The simplest and most powerful way I know of doing undo is with immutable data. With immutable, persistent data you can cheaply keep a log of previous states in order to find last known values, rewinding or whatever. Immutable data is only really an option though if your language has good support for it, imperative languages usually don't. Reproducable actions are a good alternative.
User avatar
Arch
Programmer
 
Posts: 206
Joined: 04 Jul 2009, 09:35
Has thanked: 0 time
Been thanked: 15 times

Re: Best practices for distributed Magic play

Postby silly freak » 14 May 2013, 11:09

Arch wrote:I think that an engine that can be used with any network solution is most likely not defined in such terms. There shouldn't be any need to consider networking when building the engine. The features required, from an engine point of view, for gaming over a network are the same as when gaming locally.

Regarding network architecture I'd suggest client-server. I think this is the easiest to reason about and it also addresses the cheating problem pretty well. (Use a trusted third-part server.) Client-server can also mimic client-client (duelling) setups by simply having one client host the server transparently.
In an ideal word, I think you're right. However, a real network solution always makes some assumptions that the engine has to hold, and in the worst case must even be designed around. The engine doesn't need additional features, but it does need "meta" features, for example a way to map pointers/references to some kind of ID that retains its meaning when transferred over the network.

I'll stay with a Java analogy since it's closest to me. Take serialization as an example, which is about the easiest way to do Java networking if you do more than sending plain bytes or similar. The assumption here is that your classes implement Serializable, which is something that has to happen inside the engine. And that said, Serialization doesn't even provide the features I'd want (most prominently, no Java lock-in for network communication).

Arch wrote:Cheating is tricky. I'd aim for making it impossible for client to cheat by not giving them information that can be used to cheat. The server should decide what can be viewed by each player and only transmit that data to the client. Since hidden information can not affect the game the engine would be able to function fully without it.
That is an interesting thought! I guess that making an engine that works with incomplete information is hard, but it would make some things easier afterwards. Min/Max AI definitely needs an engine, but to make the AI non-cheating, incomplete information in that engine is necessary.

Arch wrote:
Code: Select all
    End of upkeep step.
    P1 sends 'pass' to server.
    Server approves 'pass' and sends P1's 'pass' to P2 which P2 applies to it's state.
    P2 sends 'pass' to server.
    Server does the following:

       Applies the 'pass' to it's own state. Sees that P1 will draw a
       card that it has not been informed about.

       P1 is sent P2 'pass' and the top card of it's library.

       P2 does not require any more information other than that the
       'pass' action was ok.
This is working from the assumption that both the server and the clients are all using the same engine. P1 engine will move a card from library to hand, since it was revealed by the server the player can become aware of what card it is. P2 engine will move an anonymus card from library to hand (for P1), this can be any of the cards in the library since they should all be anonymus.
Am I right that you skipped the detailed explanation of the first 'pass'? If not, I think I missed something in your code. I disagree with one thing here: I think that it is reasonable for the client to know that the top card of the library was drawn, since it is a sorted zone. Similarly, if you move a public card to a known position in a sorted private zone, the client may retain that knowledge as long as the order of the sorted zone remains the same. I guess that's debatable, though. The point is, the server shouldn't be obligated to hide data that the client could validly deduce anyway.

Arch wrote:As long as all parties involved are using the same engine this answers part of your mirroring question. The same action can be applied at all locations and have the same outcome. The missing piece would be "randomly" generated things such as card/object-ids. This can be solved by using the same random generation algorithm and seed for all engine. Just distribute the seed at the start of the game and make sure you don't use any side effects in the engine.

As long you're using smart clients (that are aware of the logic) I agree that using a higher semantic level is better.
I think this completes my argument for implementing both peer-to-peer and client/server. As soon as you have two engines with complete information, passing actions is practically peer-to-peer out of the box. If clients have an incomplete information engine, you're back at the client-server model.

Random object IDs with a fixed seed is basically predictable object IDs, so there's not really a need to use a randomized method that has a (tiny, but still) chance of collisions. Anyway, what I fear is that a slight modification in the engine (just creating one ID more) might break multiplayer between these engines. I don't think that can be fully accounted for, but higher-level actions might at least help to minimize the number of IDs that need to be generated in the first place.

Arch wrote:The simplest and most powerful way I know of doing undo is with immutable data. With immutable, persistent data you can cheaply keep a log of previous states in order to find last known values, rewinding or whatever. Immutable data is only really an option though if your language has good support for it, imperative languages usually don't. Reproducable actions are a good alternative.
I think immutability and real OO design don't really mix. Imagine that a player's life total changes. To reflect this a new Player is generated. However, the Player belongs to a Team, so a new Team is generated with the new player in it, etc. I guess it's an interesting concept, but it's not a concept I know too well...
___

where's the "trust me, that will work!" switch for the compiler?
Laterna Magica - blog, forum, project, 2010/09/06 release!
silly freak
DEVELOPER
 
Posts: 598
Joined: 26 Mar 2009, 07:18
Location: Vienna, Austria
Has thanked: 93 times
Been thanked: 25 times

Re: Best practices for distributed Magic play

Postby Arch » 14 May 2013, 18:10

silly freak wrote:In an ideal word, I think you're right. However, a real network solution always makes some assumptions that the engine has to hold, and in the worst case must even be designed around. The engine doesn't need additional features, but it does need "meta" features, for example a way to map pointers/references to some kind of ID that retains its meaning when transferred over the network.

I'll stay with a Java analogy since it's closest to me. Take serialization as an example, which is about the easiest way to do Java networking if you do more than sending plain bytes or similar. The assumption here is that your classes implement Serializable, which is something that has to happen inside the engine. And that said, Serialization doesn't even provide the features I'd want (most prominently, no Java lock-in for network communication).
It's more a matter of language choice and convenience. You'd normally use a lot of references in java and those would cause these problems. With a more functional approach those pointers would already be ids and that serialization would be implemented in a non-intrusive way (see "type classes"").

But I think you are right to think like you do. The experiments I've had with trying to force functional programming onto java have not been very fruitful.





silly freak wrote:Am I right that you skipped the detailed explanation of the first 'pass'? If not, I think I missed something in your code. I disagree with one thing here: I think that it is reasonable for the client to know that the top card of the library was drawn, since it is a sorted zone. Similarly, if you move a public card to a known position in a sorted private zone, the client may retain that knowledge as long as the order of the sorted zone remains the same. I guess that's debatable, though. The point is, the server shouldn't be obligated to hide data that the client could validly deduce anyway.
Yes, it is not a complete sketch, more of a brain-dump.

The part with drawing: the P2 client would surely know that it's the top card being drawn (part of the logic). It just wouldn't know what card it is so from its point of view it would be the anonymus top card of the library being moved to hand. P1 on the other hand would be given something like:

Code: Select all
    {"action": "pass",
     "card-reveal": {"zone": "library",
                     "index": 1,
                     "card": "Mountain"}}
So when P1 get the card to hand it can be displayed correctly. What I was after was the idea that the server can see how visibility changes when actions are performed and inform the clients appropriately.

I have no doubt it would be pretty hairy to implement though.



silly freak wrote:Random object IDs with a fixed seed is basically predictable object IDs, so there's not really a need to use a randomized method that has a (tiny, but still) chance of collisions. Anyway, what I fear is that a slight modification in the engine (just creating one ID more) might break multiplayer between these engines. I don't think that can be fully accounted for, but higher-level actions might at least help to minimize the number of IDs that need to be generated in the first place.
It's true you'd better off with incremental ids or similar instead of such randomness.

Don't like the idea of trying to minimize id generation. Either it works or it doesn't. If it can break when generating 1000 ids it can break when generating 1. There could be some challenges in getting the synchonization of clients to work but that feel more like a problem that you'll either know you've solved or you haven't solved it. Expanding on my reveal message for instance.

Code: Select all
    {"action": "pass",
     "next-id-should-be": 548}
User avatar
Arch
Programmer
 
Posts: 206
Joined: 04 Jul 2009, 09:35
Has thanked: 0 time
Been thanked: 15 times

Re: Best practices for distributed Magic play

Postby silly freak » 15 May 2013, 10:55

Arch wrote:It's more a matter of language choice and convenience. You'd normally use a lot of references in java and those would cause these problems. With a more functional approach those pointers would already be ids and that serialization would be implemented in a non-intrusive way (see "type classes"").

But I think you are right to think like you do. The experiments I've had with trying to force functional programming onto java have not been very fruitful.
I've read some stuff just now, and honestly it's making me dizzy :S. I hope I will get to a point of view at some point where functional programming concepts make sense to me...

Arch wrote:Don't like the idea of trying to minimize id generation. Either it works or it doesn't. If it can break when generating 1000 ids it can break when generating 1. There could be some challenges in getting the synchonization of clients to work but that feel more like a problem that you'll either know you've solved or you haven't solved it. Expanding on my reveal message for instance.

Code: Select all
    {"action": "pass",
     "next-id-should-be": 548}
Hmm... maybe you misunderstood what I meant here: As an example, let's say you have a card with two abilities. In version 1 of the engine, the card, and both of its abilities have IDs, making 3 IDs that are generated when the card is created in the engine. In version 2, you found that your solution was not optimal, and decide that abilities do no longer need IDs, thus only one ID is created. So, version 1 and version 2 are incompatible for network play.

The realization here is that it wasn't necessary to assign IDs to abilities in the first place, even if they are important parts of the engine, because they are sufficiently described by card ID and ability index. Being strict in what gets an ID from the beginning helps keeping the engine compatible. Of course, some changes can't be compatible, but not every implementation detail should break it.

I agree that some kind of assertion to ensure engines are still (likely) mirrored is probably very handy.
___

where's the "trust me, that will work!" switch for the compiler?
Laterna Magica - blog, forum, project, 2010/09/06 release!
silly freak
DEVELOPER
 
Posts: 598
Joined: 26 Mar 2009, 07:18
Location: Vienna, Austria
Has thanked: 93 times
Been thanked: 25 times

Re: Best practices for distributed Magic play

Postby Arch » 15 May 2013, 17:30

A yes, I forgot/ignored the multiple version problem you specified in the original post. Then it makes sense.
User avatar
Arch
Programmer
 
Posts: 206
Joined: 04 Jul 2009, 09:35
Has thanked: 0 time
Been thanked: 15 times

Re: Best practices for distributed Magic play

Postby arathorn2nd » 21 May 2013, 14:10

Hello guys, I just joined the forums so I could give my two cents in this discussion.

The way I see, the best implementation for undo is serialization of game state, in a linked list or even tree graph for multiple paths if needed/wanted by AI.

I agree with the server-client thinking proposed. My suggestion is that the game engine is coded completely in server; the client code would only take care of UI, so as long all players trust the server is unbiased, the players would only get information on what the must know and/or act upon.

I've recently been thinking on how would I design and implement a game engine, but Real Life™ currently leaves no spare time to pursue this project. The ideas above are what I could come up in daydream.
arathorn2nd
 
Posts: 1
Joined: 21 May 2013, 13:08
Has thanked: 0 time
Been thanked: 0 time

Re: Best practices for distributed Magic play

Postby silly freak » 22 May 2013, 07:35

arathorn2nd wrote:Hello guys, I just joined the forums so I could give my two cents in this discussion.
Hi! nice to have you here!

arathorn2nd wrote:The way I see, the best implementation for undo is serialization of game state, in a linked list or even tree graph for multiple paths if needed/wanted by AI.
(singly) linked list and tree are not so different, and I also think these are the suitable strictures. But I don't think storing complete serialized game states is a good idea; it just wastes a lot of space. If you want to base your network protocol on that, things will get slow, and if you use some incremental protocol, you can use that for undo already. I can imagine that a complete game state every fifty or so moves might give some benefits, but seeing how fine grained Magic moves are, after every move sounds like a lot.

arathorn2nd wrote:I agree with the server-client thinking proposed. My suggestion is that the game engine is coded completely in server; the client code would only take care of UI, so as long all players trust the server is unbiased, the players would only get information on what the must know and/or act upon.
That's certainly the easiest way to do it, and most likely the way I will do it, bit this approach has one problem: AI. If you remove all game logic from the client, a client that is actually an AI will need to reinvent the game logic to do its job. By providing an engine that is capable of working with the incomplete information the client is facing, this problem can be avoided from the beginning

arathorn2nd wrote:I've recently been thinking on how would I design and implement a game engine, but Real Life™ currently leaves no spare time to pursue this project. The ideas above are what I could come up in daydream.
Anyway, good luck! maybe if some project here is really what you want, you can contribute there even if time's not enough to start your own.
___

where's the "trust me, that will work!" switch for the compiler?
Laterna Magica - blog, forum, project, 2010/09/06 release!
silly freak
DEVELOPER
 
Posts: 598
Joined: 26 Mar 2009, 07:18
Location: Vienna, Austria
Has thanked: 93 times
Been thanked: 25 times

Re: Best practices for distributed Magic play

Postby MageKing17 » 23 May 2013, 16:27

silly freak wrote:
arathorn2nd wrote:I agree with the server-client thinking proposed. My suggestion is that the game engine is coded completely in server; the client code would only take care of UI, so as long all players trust the server is unbiased, the players would only get information on what the must know and/or act upon.
That's certainly the easiest way to do it, and most likely the way I will do it, bit this approach has one problem: AI. If you remove all game logic from the client, a client that is actually an AI will need to reinvent the game logic to do its job. By providing an engine that is capable of working with the incomplete information the client is facing, this problem can be avoided from the beginning
In addition, if the client doesn't know any game logic, there's a lot of waiting for the server to respond just about every time the player clicks on anything; if the client knows something of how the game works, it can prompt the player for more-specific information without having to query the server first. I've heard people complain about MTGO suffering from this problem (but I've never played it myself, so I don't know).
User avatar
MageKing17
Programmer
 
Posts: 473
Joined: 12 Jun 2008, 20:40
Has thanked: 5 times
Been thanked: 9 times

Re: Best practices for distributed Magic play

Postby silly freak » 27 May 2013, 08:30

MageKing17 wrote:In addition, if the client doesn't know any game logic, there's a lot of waiting for the server to respond just about every time the player clicks on anything; if the client knows something of how the game works, it can prompt the player for more-specific information without having to query the server first. I've heard people complain about MTGO suffering from this problem (but I've never played it myself, so I don't know).
That's a point I've overlooked, I think I just don't care about user interfaces enough. Interestingly, isn't the internet just that? The browser is basically a dumb client, only slightly augmented by some JavaScript. I guess it got better in the last couple of years, but it's still practically only the newer, bigger sites that really do dynamic pages that perform part of the logic on the client side, right?
___

where's the "trust me, that will work!" switch for the compiler?
Laterna Magica - blog, forum, project, 2010/09/06 release!
silly freak
DEVELOPER
 
Posts: 598
Joined: 26 Mar 2009, 07:18
Location: Vienna, Austria
Has thanked: 93 times
Been thanked: 25 times

Re: Best practices for distributed Magic play

Postby Incantus » 27 May 2013, 19:59

silly freak wrote:Hi all!

After my latest attempt on a Magic engine ended after I realized that I had fundamental flaws at the lowest level, I was now thinking again about what is really necessary to support a Magic engine that will survive adding network play in different variants.

With different variants, I basically mean these features:
  • A classic client-server model, where the clients do not run the game engine themselves, but only receive the information they need (here, I don't yet demand that a malicious client couldn't cheat)
  • A true peer-to-peer solution, where the clients all run game engines, but they are kept in sync, ideally even across multiple versions of the software (to some extent of course)
  • A combination thereof, e.g. clients connected to different servers that have mirrored game engines
  • Spectators that view, but don't participate in the match (e.g. a GUI for an AI only match)
Hi Silly Freak,

Interesting to hear you face many of the same issues I did when first developing Incantus. In the end, I decided to go with the pure P2P model where each player runs a full game engine and the networking layer passes around player actions. An interesting result of this design is that the UI/network interface to the game engine is the same - all the game engine receives is player actions (here's the full set in Incantus - which was enough to implement about 95% of the Comprehensive Rules - https://github.com/Incantus/incantus/bl ... /Action.py). When a user 'hosted' a networked game, the program would run also run a small server that other players could connect to (including the hosting player - he was treated the same as any other player) and any serialized action would be broadcast to all the other players. This also enabled watching and game replays, since given a starting state (basically starting decks and a random seed) it was possible to replay any game. Also, since Magic is a turn-based game, it was fairly easy to keep all players in lock step, while still allowing the UI to remain responsive (ie, when it wasn't the player's turn - the engine would just be waiting for an action from the 'networked' player, and when it was the player's turn the engine would be waiting for an action from the UI).

I managed to decouple the engine from the UI/network using coroutines, which lets you intermesh two event loops (the UI loop and the game engine loop) while still allowing you to program both in a natural fashion. For example, the core game loop in Incantus is the following code: https://github.com/Incantus/incantus/bl ... er.py#L126). That's it! If you aren't familiar with coroutines I highly recommend learning about them - they are a bit of a mind bender but once you get them they vastly simplify asynchronous code and let you keep your logic local). So you can imagine two parallel sets of stacks (in the CS sense):

Code: Select all
     Game Engine Loop                         UI/network loop
    ----------------                        -----------------
   [  Main game loop running ]                 [ UI loop run by toolkit ]
   [  MtG's turn structure      ]                           |
                  |                                         |
                  |                                         |
   [  At some point, need input ]        ->   [ Respond to player input ]
   [  from a player            ] ---/

If you are familiar with UI code, you know that the toolkit is responsible for the core loop and you just provide callbacks to handle user events (mouse and key presses, etc). The key thing i found (and this applies to all turn-based games) is that whenever the engine required input from the player, the code would 'jump' out of the engine stack frames and into the UI stack frames. In effect, the game engine 'runs' in between event handling from the player/network.

Of course, the p2p design has issues with regards a certain type of cheating (but it prevents a different kind of cheating that happens in client-server architectures). Since all players are running a full game engine, that means every user's program has full knowledge of the entire game state (including the player's shuffled deck order and the other player's hidden information). So if a user can patch his version, it would be possible for him to reveal all hidden information. There is an interesting set of cryptographic solutions to this problem (for example, mental poker - http://en.wikipedia.org/wiki/Mental_poker), which might be amenable to use in MtG (basically the protocol allows the game to encrypt and shuffle everyone's deck, and you can only decrypt a card with the participation of all player's engines - which means that all client engines must agree that revealing that card to a particular engine is a valid operation). Unfortunately, since Incantus was just a side project, I never had a chance to implement something like that (and frankly, given the number of users Incantus had, that kind of cheating was a very minor concern).

silly freak wrote:
Besides that, there are a few other requirements:
  • For client-server, the clients shouldn't be able to cheat. That can be quite sophisticated. For example, a client should be able to tell that a library was shuffled, but must not be able to infer from any IDs what the shuffling did to the library.
For client server this is pretty easy - the server only reveals pertinent information to the client. As long as the client can only generate user actions, which the server is free to reject, there's basically no issues of cheating. Of course this approach leads to high latency when every action has to be validated by the server (and it makes it difficult to provide UI niceties that depend on game state, such as highlighting valid targets for an effect, etc.)

silly freak wrote:
  • For mirroring, it shouldn't be necessary to transmit game states, but rather only actions that modify the game state (or is there a third option?) Implications are that object references must be somehow valid across the network, and even worse, objects created through the action must receive the same ID on all mirrored engines.
    Ideally, the transmitted actions are still on the semantic "Magic" level, e.g. "attack with these creatures" instead of "tap these creatures and add them to combat." I don't know, but I feel that would be the better way to transmit actions.

This is pretty easy given the architecture I outlined above. Basically you just need to wrap player actions in higher semantic Magic actions.

silly freak wrote:
With transmittable actions, there come a few other features that would be great and shouldn't add too much additional work at that point; undo and game persistence, namely. Undo adds the possibility for Min/Max and possibly other AI algorithms, besides being nice for the user, and game persistence is practically self implementing when even a remote engine can be initialized with a list of actions taken.


I thought I wouldn't be the only one interested in these things, and I haven't sorted out what is the right thing to implement first so that my work stands on a solid foundation. Do any of you have ideas on these topics, already implemented or otherwise? Do you see other questions that I forgot to ask?
I haven't followed recent developments in other magic programs, but I still think Incantus is the only program to have true networking support with full game engine (most other programs that let you play over the wire don't enforce any rules). If you have any questions about parts of Incantus I'd be happy to answer them.
Incantus
DEVELOPER
 
Posts: 267
Joined: 29 May 2008, 15:53
Has thanked: 0 time
Been thanked: 3 times

Re: Best practices for distributed Magic play

Postby silly freak » 01 Jun 2013, 19:20

Incantus wrote:This also enabled watching and game replays, since given a starting state (basically starting decks and a random seed) it was possible to replay any game. Also, since Magic is a turn-based game, it was fairly easy to keep all players in lock step, while still allowing the UI to remain responsive (ie, when it wasn't the player's turn - the engine would just be waiting for an action from the 'networked' player, and when it was the player's turn the engine would be waiting for an action from the UI).
Hi! It's nice to hear that there's already a working engine using the P2P approach out there. Incantus looks really clean from what I can see; I should really take a closer look at it.

Incantus wrote:I managed to decouple the engine from the UI/network using coroutines, which lets you intermesh two event loops (the UI loop and the game engine loop) while still allowing you to program both in a natural fashion. For example, the core game loop in Incantus is the following code: https://github.com/Incantus/incantus/bl ... er.py#L126). That's it! If you aren't familiar with coroutines I highly recommend learning about them - they are a bit of a mind bender but once you get them they vastly simplify asynchronous code and let you keep your logic local).

If you are familiar with UI code, you know that the toolkit is responsible for the core loop and you just provide callbacks to handle user events (mouse and key presses, etc). The key thing i found (and this applies to all turn-based games) is that whenever the engine required input from the player, the code would 'jump' out of the engine stack frames and into the UI stack frames. In effect, the game engine 'runs' in between event handling from the player/network.
I know of coroutines, at least theoretically. I think they are a great thing, but I didn't have the opportunity to work with them yet.

Incantus wrote:I haven't followed recent developments in other magic programs, but I still think Incantus is the only program to have true networking support with full game engine (most other programs that let you play over the wire don't enforce any rules). If you have any questions about parts of Incantus I'd be happy to answer them.
Thank you, I'll probably come back to your offer. Currently, I'm experimenting with Tic Tac Toe and Uno as simpler games for testing my network protocol and action approaches; when Uno works properly, I'll try to modify it to use incomplete information engines or even a P2P anti-cheating approach such as mental poker.
___

where's the "trust me, that will work!" switch for the compiler?
Laterna Magica - blog, forum, project, 2010/09/06 release!
silly freak
DEVELOPER
 
Posts: 598
Joined: 26 Mar 2009, 07:18
Location: Vienna, Austria
Has thanked: 93 times
Been thanked: 25 times

Re: Best practices for distributed Magic play

Postby gmzombie » 12 Jun 2013, 04:57

while im not a programmer by any means have you looked into the manalink game on here. i mean it does already have the support for online playing but some of the code got wacked in an update. manalink itself has a vast stock of cards and actul rules enforcement but would require a knowledge in ASM and C. i actually have all of the updates and i believe i know when the online portion of the game was effected. but like i said i dont code. i just help whenever possible and hack.
can I maze of ith your snowstorm?

http://home.comcast.net/~gmzombie/index.html old stuff in here. don't use this stuff right now till I get time to get back into it and readjust.
gmzombie
 
Posts: 857
Joined: 26 Feb 2009, 01:05
Location: Wyoming, Mi
Has thanked: 200 times
Been thanked: 51 times

Re: Best practices for distributed Magic play

Postby silly freak » 09 Aug 2013, 18:52

If anyone is interested, my current efforts are pretty fruitful. You can read it on my blog (permalink).

In a nutshell, I wrote an extension for protobuf that lets me exchange object-oriented, polymorphic data with small overhead. On top of that, I wrote a library that manages an Engine, consisting of Entities. The engine changes between States by executing Actions, and multiple Engines can be synchronized by exchanging these States, which encapsulate not the changed data but only the Actions; the data exchange format is my protobuf extension, and is independent from the underlying network protocol. I added a pluggable network implementation using JGroups, which does things like reliable, wide-area multicasts.

I tested it by creating a simple TicTacToe-application. It's not perfect, neither in implementing the rules (any client can make moves for the current player), nor in adhering to the Action contract (the PlacePieceAction can't be properly reverted). Both is just a matter of effort; it still shows that the approach does work as intended.
___

where's the "trust me, that will work!" switch for the compiler?
Laterna Magica - blog, forum, project, 2010/09/06 release!
silly freak
DEVELOPER
 
Posts: 598
Joined: 26 Mar 2009, 07:18
Location: Vienna, Austria
Has thanked: 93 times
Been thanked: 25 times


Return to Magic Rules Engine Programming

Who is online

Users browsing this forum: No registered users and 14 guests


Who is online

In total there are 14 users online :: 0 registered, 0 hidden and 14 guests (based on users active over the past 10 minutes)
Most users ever online was 4143 on 23 Jan 2024, 08:21

Users browsing this forum: No registered users and 14 guests

Login Form