First up, players: each player simulates their own player authoritatively and transmits their position to the host. Yes this allows for cheating technically, but this is the fastest way to deduce positions and makes your own movements feel instant. If your friend is cheating just don't play with them. I'd honestly be surprised if someone makes a hacked client for this goofy ahh game.
The ball: whoever last hit the ball gets ball position authority. This is in order to make dribbling the ball feel instant and have no delay between hits. I have tried about 15-20 methods to keep the ball in sync, and this is the best way so far, every other method either creates jitter, extreme desync, or makes the game unplayable past ~50 ping. With this setup the game is playable up to ~120 ping.
Bullets: bullets shot by either player are spawned by themselves and sent to the peer as a single packet containing a exact position, angle, and speed. Bullets shot by turrets are requested by the host as a global turret update. Bullet shot by bosses are computed on the host and sent as a shoot packet to the guest. Bullets are displayed on both player's screens, but are just visual on the guest's side, all damage is computed authoritatively on the host and sent to the guest as a health update. Bullets are only synced over the network at creation, since sending real time positions is extremely network intensive for a large amount of objects considering the game can stably support even thousands of bullets on screen at once. Fun fact: bullets are differentiated by different shooter IDs, being 0 (turret), 1 (player1), 2 (player2), and 8 (boss).
Teleporters: different objects passing through teleporters are computed differently. Players both compute their own teleports and just send their position to the peer. Bullets are computed separately on the host and guest, but their teleports are synced up by a seeding algorithm. There are different seeding algorithms for player-shot and turret-shot bullets. Boss bullets do not teleport because I have so far found no way to eliminate desync with this.
The networking code uses a giant switch statement (of ints 0-200) that determines what type of packet was just sent or received. Not all packet types are used, and I don't expect to ever use all 200 spots. Every action in the game uses its own packet type to sync up to the other player, like boss attacks and positions.
All the data for a level is stored in a single .mzlvl file, and this file is designed to be easily readable both by the game and by humans. Levels are designed to be manually editable (but I obviously recommend using the in-game editor). Download an example level file here. A level file contains 3 main parts:
The level header has all your level settings. When a game update creates new settings, they are simply filled in with reasonable default values, keeping all old levels compatible with all future versions of the game. Do not change around the order of these values!
This does nothing in the actual game, it is simply for human readability, and for if you get one of these files without having any idea what it is for or what game it's from. The "TILE INDEX" part still fits under this category, it doesn't actually get read by the game in any meaningful way.
This is the physical grid of objects in your level. Levels are all in an aspect ratio of 4 wide x 3 tall, but since most text editors add more space between rows than columns, they look very goofy when viewed like this.