+1. I had a few ideas for user-driven content that are sadly limited by the lack of networking. Android-esque permissions with a suitable warning from Playdate seems like a sufficient compromise to handle security concerns.
I would love the the prospect of being able to add real-time multiplayer to a project. Has there been any word as to what Panic has in mind for support?
What if networking allows only a fixed network API that can only send pre-defined fixed packet types?
Data/requests can only goto a nearby Playdate in a mesh configuration, or through a Panic server. It seems reasonably straightforward. Just don't allow unfettered network comms.
That should be enough to allow multiple players, and stop abuse.
I could work out an API for disussion if people think it's worthwhile.
This seems like a cool approach! Has any other device or service implemented it in that way?
Maybe the Switch does something like this in local (non internet) mode. The wifi range is really low in this mode, sometimes we can't get it to go > ~3m.
Thinking about this a bit more, here's a few API calls off the top of my head:
- int startLocalNetwork()
- returns your local network game player-id (or maybe the API just holds this for you?)
- -1 on failure
- void stopLocalNetwork()
- List findLocalPlayers( int game_id, string player_name )
- returns a list of player-IDs wishing to network play the given game
- only works for players on the local subnet / ad-hoc network. Not internet play.
- software wanting to network play, calls this function. So players A and B both call this, and A is returned B, and B is returned A.
- the <player_name> is optional, but is a human-readable string able to be set by the player. Profanity filtered (which is impossible IMHO) if necessary.
- addPlayer( List players, int player_id );
- Add the given remote player to some Corpus of "current players".
- Allows certain players to not be added?
- sendEvent( List players, int event_id, float at_x, float at_y, uint32 data_a, uint32 data_b );
- sendEventWithData( List players, int player_id, int event_id, float at_x, float at_y, octet data );
- Send the event to every remote game accepted into the List.
List might have a
player_count and an array of player
ids. (say with a max of 16?)
I would use fixed size packets for all this, they're small with a known size. In terms of underlying protocol, the first byte of the packet would be it's type/size, so the API knows exactly how much data to expect - and can junk the packet if the remainder it doesn't arrive in a timely manner. With such small packets they'd probably all arrive in a single chunk anyway.
The wifi MTU can be set small in this mode.
The idea with the Event and Location, is that the game only needs to know when an entity is started. Say for whatever reason, a new object appears in the local game. It calls
sendEvent() with the "new object" command ID (any number), and a location (in any sense). The two data parameters can be used for whatever else is needed. Say direction and speed, colour, flavour, hit-points, etc. etc.
For more involved events, there is
sendEventWithData(). This also keeps it simple. There is some extra space for packing bigger payload, but it's still small enough to transmit quickly.
It should never be necessary to transmit say, game assets, as the "other" game already has these. Maybe we'd add some more special-purpose commands, but pretty much everything can be done with these simple events. If you've ever looked at underneath the messaging queue for any window based OS, they use exactly this sort of simple event-messaging. (Back in former times, one had to process these messages manually.) Keeping the packets small should at least hinder sending recorded voices, etc. Doesn't prevent it though, but it only goes to the local sub-net.
Anyway, just some initial thoughts.
Instead if adding a player, wouldn't it make more sense for a player to join a local network ?
Yeah, you're right. You only ever need to know about the current game running. I guess there could potentially be multiple local sets of games running though.
NetGameIdList findLocalNetworks( int current_game_id ); boolean joinLocalNetwork( int net_game_id );
Returns a list of network games in progress. [Q: Do game-networks need a human-readable name? What if there's say 3 in progress, how can the user pick which to join? Is this really a problem?]
If you're the first person starting though (even by a nanosecond), this would be an empty List. So perhaps the API can handle this internally by creating a new network-game identifier once the various timeouts & re-tries have failed to find a game. Internally there should be some random-backoff when colliding the "start new game" network token.
Once you have a game-ID, events are sent received against this ID. This makes the
sendEvent() functions something like:
sendEvent( net_game_id, event_id, at_x, at_y, data_a, data_b );
It would be good if the networking API could just send received events directly into the game event queue. Internally I would just keep a local queue/buffer, filling it with a network-handle
select() (immediate timeout). Since we know how big every message is, it's easy to know when a full message is received. Pull those bytes out of the incoming queue, form them into a "Network Event" and post it out the event queue.
I was thinking about the "player-adding" in terms of refusing a player access. Maybe we don't need this at all.
Ugh, chores. BBL.
This sounds very familiar from Nintendo DS. I'll see if there's any information about how they did it.
I'm not sure how helpful this is as it is overkill, however, I found that mirror for Unity was well done.
Their API doesn't sound too disimilar to how @UrlOfSandwich described it with ConnectHost(), Send(), and AddPlayer() etc.