It also makes sense to put the client’s actions into a list, so that whenever they try to do something their action gets added to this list, which gets sent to the server periodically. Because of the nature of lag spikes, the server will receive the list irregularly, and as a result must intelligently decide how many queued actions it should execute based upon how long the latest lag spike was, as well as the client’s fps vs. the server’s fps (if the client is going twice as fast, then only half of their movement should be considered by the server).
This approach is typically good for movement, whereas it might get hokey for bullet firing and the like. It is usually okay however if you restrict less common actions (like jumping, or bullets, or whatever) until the server has received the message and sent it back, and the lag won’t seem too bad.