Netcode in a nutshell
=====================

uint32 network_current_frame <-- Local frame counter
uint32 server_current_frame  <-- Frame counter recevied from server

uint8 local_history[32]  <-- circle buffer of local player's inputs
uint8 remote_history[32] <-- circle buffer of remote player's inputs
(may refere to "both at the same time", simply by "history")

// Executed once before first tick of the game
init {
	network_current_frame = 0
	clear history
}

// Executed before every game tick
tick {
	local_history[network_current_frame+INPUT_LAG] = controller_a_btns
	if controller state changed OR it's been a long time {
		send controller state to server
	}
	if received game state from server {
		server_current_frame = value from message
		copy inputs buffer from message to input history
			starting at history[server_current_frame+1]
		copy game state from message
		copy immediate inputs from message
			controller_X_btns = value in message
			remote_history[server_current_frame] = physical state from message

		if (network_current_frame > server_current_frame) {
			network_frame_diff = network_current_frame - server_current_frame
			while (network_frame_diff != 0) {
				update controller states
					controller_X_last_frame_btns = controller_X_btns
					controller_X_btns = history[server_current_frame]
				call game_tick()
				--network_frame_diff
			}
		}else {
			network_current_frame = server_current_frame
		}
		controller_X_last_frame_btns = controller_X_btns
	}
	controller_X_btns = history[network_current_frame]
	++network_current_frame
	//NOTE: after returning, game_tick() will be executed
}

Special case of the screen state
================================

Some visual effects may impact the screen in ways that we cannot fix at will. Notably by writing in the nametable.

Such effects conflict with rollback, as we want to display the screen as it should be, even if we missed the frame where nametable have been modified.

Goals::

 * No nt_buffer generated during rollback
 * Server can run in rollback mode without special code handling that it is the server
 * The screen always ends up being in the expected state

Idea::

 * We have in memory the "ideal" screen state and the "actual" screen state
 * When both missmatch, actions have to be taken to "repair" screen
 * As actions may be heavy and require multiple frames, "actual screen state" may be more than just a mirror of "ideal"

Ideal screen state
------------------

Definition: How the screen should be.

 * Present in NewGameState STNP messages
 * Modified as usual in rollback mode

stage_fade_level => 0 (darkest) to 4 (normal)
stage_screen_effect => 0 (screen is as usual), anything else (screen effect playing)

Current screen state
--------------------

Definition: How the screen actually is

 * Not in NewGameState STNP messages
 * Not modified on rollback
 * Actions about it are done asynchronously in tick routines

``stage_current_fade_level``
 * directly like fade_level
 * if current_fade_level != fade_level and stage_screen_effect == 0, we must update palette
``stage_restore_screen_step``
 * set to zero each time stage_screen_effect is set to >= 1 (even by netcode)
 * Stage code is responsible to repair screen if it has been set to 0, and stage_screen_effect is 0
 * Keep it to zero as long as stage_screen_effect is >= 1

Responsibilities of code doing screen effects
---------------------------------------------

Must::
* Increment stage_screen_effect on start (even in rollback mode)
* Decrement stage_screen_effect on end (even in rollback mode)
* Set stage_restore_screen_step to zero on start (even in rollback mode)

Should::
* Do not generate nametable buffers in rollback mode
* Show clean animation, even if frames are skipped (or at least not too visible artifacts)
  * It may imply to redraw the full effect (including palettes) regularily
